]> Untitled Git - lemmy-ui.git/commitdiff
Merge remote-tracking branch 'origin/main' into feat/add-post-body-preview-to-desktop
authorJay Sitter <jay@jaysitter.com>
Thu, 22 Jun 2023 21:37:10 +0000 (17:37 -0400)
committerJay Sitter <jay@jaysitter.com>
Thu, 22 Jun 2023 21:37:10 +0000 (17:37 -0400)
* origin/main: (40 commits)
  Adding jsit to codeowners.
  Cleanup, only check for /u/ if /c/ and /m/ checks fail
  Rename function to be more generic, since it parses users
  Typescript linter fixes
  bandaid fix our video embeds
  Remove pipe from community link regex
  Add missing classes
  Use shorter regex in community link parser
  Move regex pattern to config
  Update community link markdown parsing
  Fix avatar alignment issue (#1475)
  Omit user-scalable to use default
  Update getHttpBase dependency reference
  Enable users to zoom on mobile
  rethink it a bit
  rethink it a bit
  add fallback style tag
  move env utils into folder
  fix capitalization (#1467)
  Fix buildThemeList() function to ensure no duplicates (#1466)
  ...

97 files changed:
.github/CODEOWNERS
package.json
src/client/index.tsx
src/server/handlers/catch-all-handler.tsx
src/server/handlers/manifest-handler.ts
src/server/index.tsx
src/server/utils/build-themes-list.ts
src/server/utils/create-ssr-html.tsx
src/server/utils/fetch-icon-png.ts
src/server/utils/generate-manifest-json.ts
src/shared/components/app/app.tsx
src/shared/components/app/error-page.tsx
src/shared/components/app/footer.tsx
src/shared/components/app/navbar.tsx
src/shared/components/comment/comment-form.tsx
src/shared/components/comment/comment-node.tsx
src/shared/components/comment/comment-report.tsx
src/shared/components/common/badges.tsx
src/shared/components/common/comment-sort-select.tsx
src/shared/components/common/data-type-select.tsx
src/shared/components/common/emoji-picker.tsx
src/shared/components/common/html-tags.tsx
src/shared/components/common/icon.tsx
src/shared/components/common/image-upload-form.tsx
src/shared/components/common/language-select.tsx
src/shared/components/common/listing-type-select.tsx
src/shared/components/common/markdown-textarea.tsx
src/shared/components/common/moment-time.tsx
src/shared/components/common/navigation-prompt.tsx
src/shared/components/common/paginator.tsx
src/shared/components/common/pictrs-image.tsx
src/shared/components/common/registration-application.tsx
src/shared/components/common/searchable-select.tsx
src/shared/components/common/sort-select.tsx
src/shared/components/community/communities.tsx
src/shared/components/community/community-form.tsx
src/shared/components/community/community.tsx
src/shared/components/community/create-community.tsx
src/shared/components/community/sidebar.tsx
src/shared/components/home/admin-settings.tsx
src/shared/components/home/emojis-form.tsx
src/shared/components/home/home.tsx
src/shared/components/home/instances.tsx
src/shared/components/home/legal.tsx
src/shared/components/home/login.tsx
src/shared/components/home/rate-limit-form.tsx
src/shared/components/home/setup.tsx
src/shared/components/home/signup.tsx
src/shared/components/home/site-form.tsx
src/shared/components/home/site-sidebar.tsx
src/shared/components/home/tagline-form.tsx
src/shared/components/modlog.tsx
src/shared/components/person/cake-day.tsx
src/shared/components/person/inbox.tsx
src/shared/components/person/password-change.tsx
src/shared/components/person/profile.tsx
src/shared/components/person/registration-applications.tsx
src/shared/components/person/reports.tsx
src/shared/components/person/settings.tsx
src/shared/components/person/verify-email.tsx
src/shared/components/post/create-post.tsx
src/shared/components/post/metadata-card.tsx
src/shared/components/post/post-form.tsx
src/shared/components/post/post-listing.tsx
src/shared/components/post/post-listings.tsx
src/shared/components/post/post-report.tsx
src/shared/components/post/post.tsx
src/shared/components/private_message/create-private-message.tsx
src/shared/components/private_message/private-message-form.tsx
src/shared/components/private_message/private-message-report.tsx
src/shared/components/private_message/private-message.tsx
src/shared/components/search.tsx
src/shared/config.ts
src/shared/env.ts [deleted file]
src/shared/markdown.ts
src/shared/services/HttpService.ts
src/shared/services/I18NextService.ts [moved from src/shared/i18next.ts with 53% similarity]
src/shared/services/UserService.ts
src/shared/services/index.ts
src/shared/toast.ts
src/shared/utils/app/convert-comment-sort-type.ts
src/shared/utils/app/initialize-site.ts
src/shared/utils/app/update-community-block.ts
src/shared/utils/app/update-person-block.ts
src/shared/utils/browser/restore-scroll-position.ts
src/shared/utils/browser/save-scroll-position.ts
src/shared/utils/env/get-base-local.ts [new file with mode: 0644]
src/shared/utils/env/get-external-host.ts [new file with mode: 0644]
src/shared/utils/env/get-host.ts [new file with mode: 0644]
src/shared/utils/env/get-http-base-external.ts [new file with mode: 0644]
src/shared/utils/env/get-http-base-internal.ts [new file with mode: 0644]
src/shared/utils/env/get-http-base.ts [new file with mode: 0644]
src/shared/utils/env/get-internal-host.ts [new file with mode: 0644]
src/shared/utils/env/get-secure.ts [new file with mode: 0644]
src/shared/utils/env/http-external-path.ts [new file with mode: 0644]
src/shared/utils/env/index.ts [new file with mode: 0644]
src/shared/utils/env/is-https.ts [new file with mode: 0644]

index 76916e604aeda28ef62a8e3bbd4ab7f8d13a8e82..ee3d7a5420401eed580da61fa6ffd41ea9154255 100644 (file)
@@ -1 +1 @@
-* @dessalines @SleeplessOne1917 @alectrocute
+* @dessalines @SleeplessOne1917 @alectrocute @jsit
index 9a285463d74f9f39a89ce27a4c048ebdef2465d7..9e7a0f1b2f79b369b609f63f0bcb1b36b9e4f2e1 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "lemmy-ui",
-  "version": "0.18.0-rc.4",
+  "version": "0.18.0-rc.6",
   "description": "An isomorphic UI for lemmy",
   "repository": "https://github.com/LemmyNet/lemmy-ui",
   "license": "AGPL-3.0",
     "translations:update": "git submodule update --remote --recursive"
   },
   "lint-staged": {
-    "*.{ts,tsx,js}": ["prettier --write", "eslint --fix"],
-    "*.{css, scss}": ["prettier --write"],
-    "package.json": ["sortpack"]
+    "*.{ts,tsx,js}": [
+      "prettier --write",
+      "eslint --fix"
+    ],
+    "*.{css, scss}": [
+      "prettier --write"
+    ],
+    "package.json": [
+      "sortpack"
+    ]
   },
   "dependencies": {
     "@babel/plugin-proposal-decorators": "^7.21.0",
index 4ff794eca850952e590632da944e79d8070d0a85..eb2bb80fe2e515221d127d6a5a2f62c179884b5a 100644 (file)
@@ -2,7 +2,7 @@ import { initializeSite } from "@utils/app";
 import { hydrate } from "inferno-hydrate";
 import { Router } from "inferno-router";
 import { App } from "../shared/components/app/app";
-import { HistoryService } from "../shared/services/HistoryService";
+import { HistoryService } from "../shared/services";
 
 import "bootstrap/js/dist/collapse";
 import "bootstrap/js/dist/dropdown";
index b9ff13bfb3de92732fdb45baa44abde3aae997e3..f22b8a1734f55ed84475dc3d1bccb30045f9fd43 100644 (file)
@@ -1,12 +1,13 @@
 import { initializeSite, isAuthPath } from "@utils/app";
+import { getHttpBaseInternal } from "@utils/env";
 import { ErrorPageData } from "@utils/types";
+import fetch from "cross-fetch";
 import type { Request, Response } from "express";
 import { StaticRouter, matchPath } from "inferno-router";
 import { renderToString } from "inferno-server";
 import IsomorphicCookie from "isomorphic-cookie";
 import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client";
 import { App } from "../../shared/components/app/app";
-import { getHttpBaseInternal } from "../../shared/env";
 import {
   InitialFetchRequest,
   IsoDataOptionalSite,
index 55c7b64fead6aa0af2ffabb7a5984c4cc54b8cd0..c1756f7570661fadad4e532336b1a767c439d5c5 100644 (file)
@@ -1,6 +1,7 @@
+import { getHttpBaseExternal, getHttpBaseInternal } from "@utils/env";
+import fetch from "cross-fetch";
 import type { Request, Response } from "express";
 import { LemmyHttp } from "lemmy-js-client";
-import { getHttpBaseInternal } from "../../shared/env";
 import { wrapClient } from "../../shared/services/HttpService";
 import generateManifestJson from "../utils/generate-manifest-json";
 import { setForwardedHeaders } from "../utils/set-forwarded-headers";
@@ -9,7 +10,7 @@ let manifest: Awaited<ReturnType<typeof generateManifestJson>> | undefined =
   undefined;
 
 export default async (req: Request, res: Response) => {
-  if (!manifest) {
+  if (!manifest || manifest.start_url !== getHttpBaseExternal()) {
     const headers = setForwardedHeaders(req.headers);
     const client = wrapClient(
       new LemmyHttp(getHttpBaseInternal(), { fetchFunction: fetch, headers })
index 144b596ec618f5effa7d2f7f4b4edf60b4309bd9..b65506b0e6c4a3709e9b7ccd2dfd12f173bfaea3 100644 (file)
@@ -25,7 +25,7 @@ if (!process.env["LEMMY_UI_DISABLE_CSP"] && !process.env["LEMMY_UI_DEBUG"]) {
 
 server.get("/robots.txt", RobotsHandler);
 server.get("/service-worker.js", ServiceWorkerHandler);
-server.get("/manifest", ManifestHandler);
+server.get("/manifest.webmanifest", ManifestHandler);
 server.get("/css/themes/:name", ThemeHandler);
 server.get("/css/themelist", ThemesListHandler);
 server.get("/*", CatchAllHandler);
index 73dc53bcad4d3df324c7e436a8d520da7ef2c5cb..74b910c767d86ed24973886adb4d387a05dd5b0e 100644 (file)
@@ -4,15 +4,20 @@ import { readdir } from "fs/promises";
 const extraThemesFolder =
   process.env["LEMMY_UI_EXTRA_THEMES_FOLDER"] || "./extra_themes";
 
-const themes = ["darkly", "darkly-red", "litely", "litely-red"];
+const themes: ReadonlyArray<string> = [
+  "darkly",
+  "darkly-red",
+  "litely",
+  "litely-red",
+];
 
-export async function buildThemeList(): Promise<string[]> {
+export async function buildThemeList(): Promise<ReadonlyArray<string>> {
   if (existsSync(extraThemesFolder)) {
     const dirThemes = await readdir(extraThemesFolder);
     const cssThemes = dirThemes
       .filter(d => d.endsWith(".css"))
       .map(d => d.replace(".css", ""));
-    themes.push(...cssThemes);
+    return themes.concat(cssThemes);
   }
   return themes;
 }
index 569d83acaaf3814b0569a129152ff9e299d39ed2..13775981b2972d97ff1561b264bbd0093b6d7ee2 100644 (file)
@@ -4,6 +4,7 @@ import serialize from "serialize-javascript";
 import sharp from "sharp";
 import { favIconPngUrl, favIconUrl } from "../../shared/config";
 import { ILemmyConfig, IsoDataOptionalSite } from "../../shared/interfaces";
+import { buildThemeList } from "./build-themes-list";
 import { fetchIconPng } from "./fetch-icon-png";
 
 const customHtmlHeader = process.env["LEMMY_UI_CUSTOM_HTML_HEADER"] || "";
@@ -16,6 +17,10 @@ export async function createSsrHtml(
 ) {
   const site = isoData.site_res;
 
+  const fallbackTheme = `<link rel="stylesheet" type="text/css" href="/css/themes/${
+    (await buildThemeList())[0]
+  }.css" />`;
+
   if (!appleTouchIcon) {
     appleTouchIcon = site?.site_view.site.icon
       ? `data:image/png;base64,${sharp(
@@ -68,7 +73,7 @@ export async function createSsrHtml(
     <!-- Required meta tags -->
     <meta name="Description" content="Lemmy">
     <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <link
        id="favicon"
        rel="shortcut icon"
@@ -77,7 +82,7 @@ export async function createSsrHtml(
      />
   
     <!-- Web app manifest -->
-    <link rel="manifest" href="/manifest" />
+    <link rel="manifest" href="/manifest.webmanifest" />
     <link rel="apple-touch-icon" href=${appleTouchIcon} />
     <link rel="apple-touch-startup-image" href=${appleTouchIcon} />
   
@@ -85,7 +90,7 @@ export async function createSsrHtml(
     <link rel="stylesheet" type="text/css" href="/static/styles/styles.css" />
   
     <!-- Current theme and more -->
-    ${helmet.link.toString()}
+    ${helmet.link.toString() || fallbackTheme}
     
     </head>
   
index 12b09e7040d0283ce81af7730b12ea3b72ee435b..75497a21878a7453644f421bec2a05fec1c5c0c0 100644 (file)
@@ -1,3 +1,5 @@
+import fetch from "cross-fetch";
+
 export async function fetchIconPng(iconUrl: string) {
   return await fetch(iconUrl)
     .then(res => res.blob())
index b03fd87bdf385102101396f552a5bdc3708efac9..2f9d8b801c6b48e87572d88f617b412f81a6a86f 100644 (file)
@@ -1,8 +1,8 @@
+import { getHttpBaseExternal } from "@utils/env";
 import { readFile } from "fs/promises";
 import { GetSiteResponse } from "lemmy-js-client";
 import path from "path";
 import sharp from "sharp";
-import { getHttpBaseExternal } from "../../shared/env";
 import { fetchIconPng } from "./fetch-icon-png";
 
 const iconSizes = [72, 96, 128, 144, 152, 192, 384, 512];
index e615bb3a4fe8466a118210979f2f416481e8e54d..e50a70709fe572116e150315f2cf7ca2f316374d 100644 (file)
@@ -2,9 +2,9 @@ import { isAuthPath, setIsoData } from "@utils/app";
 import { Component, RefObject, createRef, linkEvent } from "inferno";
 import { Provider } from "inferno-i18next-dess";
 import { Route, Switch } from "inferno-router";
-import { i18n } from "../../i18next";
 import { IsoDataOptionalSite } from "../../interfaces";
 import { routes } from "../../routes";
+import { I18NextService } from "../../services";
 import AuthGuard from "../common/auth-guard";
 import ErrorGuard from "../common/error-guard";
 import { ErrorPage } from "./error-page";
@@ -31,13 +31,13 @@ export class App extends Component<any, any> {
 
     return (
       <>
-        <Provider i18next={i18n}>
+        <Provider i18next={I18NextService.i18n}>
           <div id="app" className="lemmy-site">
             <a
               className="skip-link bg-light text-dark p-2 text-decoration-none position-absolute start-0 z-3"
               onClick={linkEvent(this, this.handleJumpToContent)}
             >
-              ${i18n.t("jump_to_content", "Jump to content")}
+              ${I18NextService.i18n.t("jump_to_content", "Jump to content")}
             </a>
             {siteView && (
               <Theme defaultTheme={siteView.local_site.default_theme} />
index 7d4e297053eca00021d7c14366f6167b5112373c..ec352d4288dfeefbe628169b379e0b422575ec7a 100644 (file)
@@ -2,8 +2,8 @@ import { setIsoData } from "@utils/app";
 import { Component } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Link } from "inferno-router";
-import { i18n } from "../../i18next";
 import { IsoDataOptionalSite } from "../../interfaces";
+import { I18NextService } from "../../services";
 
 export class ErrorPage extends Component<any, any> {
   private isoData: IsoDataOptionalSite = setIsoData(this.context);
@@ -19,8 +19,8 @@ export class ErrorPage extends Component<any, any> {
       <div className="error-page container-lg text-center">
         <h1>
           {errorPageData
-            ? i18n.t("error_page_title")
-            : i18n.t("not_found_page_title")}
+            ? I18NextService.i18n.t("error_page_title")
+            : I18NextService.i18n.t("not_found_page_title")}
         </h1>
         {errorPageData ? (
           <T i18nKey="error_page_paragraph" className="p-4" parent="p">
@@ -28,18 +28,18 @@ export class ErrorPage extends Component<any, any> {
             <a href="https://matrix.to/#/#lemmy-space:matrix.org">#</a>#
           </T>
         ) : (
-          <p>{i18n.t("not_found_page_message")}</p>
+          <p>{I18NextService.i18n.t("not_found_page_message")}</p>
         )}
         {!errorPageData && (
           <Link to="/" replace>
-            {i18n.t("not_found_return_home_button")}
+            {I18NextService.i18n.t("not_found_return_home_button")}
           </Link>
         )}
         {errorPageData?.adminMatrixIds &&
           errorPageData.adminMatrixIds.length > 0 && (
             <>
               <div>
-                {i18n.t("error_page_admin_matrix", {
+                {I18NextService.i18n.t("error_page_admin_matrix", {
                   instance:
                     this.isoData.site_res?.site_view.site.name ??
                     "this instance",
index 601045a408708d2c7ff296e99aa6848ccbfae896..6409ef3563a939dde0ff7c9b0e425da3cd0bc254 100644 (file)
@@ -2,7 +2,7 @@ import { Component } from "inferno";
 import { NavLink } from "inferno-router";
 import { GetSiteResponse } from "lemmy-js-client";
 import { docsUrl, joinLemmyUrl, repoUrl } from "../../config";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { VERSION } from "../../version";
 
 interface FooterProps {
@@ -29,36 +29,36 @@ export class Footer extends Component<FooterProps, any> {
             </li>
             <li className="nav-item">
               <NavLink className="nav-link" to="/modlog">
-                {i18n.t("modlog")}
+                {I18NextService.i18n.t("modlog")}
               </NavLink>
             </li>
             {this.props.site?.site_view.local_site.legal_information && (
               <li className="nav-item">
                 <NavLink className="nav-link" to="/legal">
-                  {i18n.t("legal_information")}
+                  {I18NextService.i18n.t("legal_information")}
                 </NavLink>
               </li>
             )}
             {this.props.site?.site_view.local_site.federation_enabled && (
               <li className="nav-item">
                 <NavLink className="nav-link" to="/instances">
-                  {i18n.t("instances")}
+                  {I18NextService.i18n.t("instances")}
                 </NavLink>
               </li>
             )}
             <li className="nav-item">
               <a className="nav-link" href={docsUrl}>
-                {i18n.t("docs")}
+                {I18NextService.i18n.t("docs")}
               </a>
             </li>
             <li className="nav-item">
               <a className="nav-link" href={repoUrl}>
-                {i18n.t("code")}
+                {I18NextService.i18n.t("code")}
               </a>
             </li>
             <li className="nav-item">
               <a className="nav-link" href={joinLemmyUrl}>
-                {i18n.t("join_lemmy")}
+                {I18NextService.i18n.t("join_lemmy")}
               </a>
             </li>
           </ul>
index 12ca05db9cb504a49cc236ecbbd5c0c38c96a7b1..2ede00e183be2d0234adc3c56c205324f1fac140 100644 (file)
@@ -11,8 +11,7 @@ import {
   GetUnreadRegistrationApplicationCountResponse,
 } from "lemmy-js-client";
 import { donateLemmyUrl, updateUnreadCountsInterval } from "../../config";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { Icon } from "../common/icon";
@@ -102,7 +101,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
               <NavLink
                 to="/inbox"
                 className="p-1 nav-link border-0 nav-messages"
-                title={i18n.t("unread_messages", {
+                title={I18NextService.i18n.t("unread_messages", {
                   count: Number(this.state.unreadApplicationCountRes.state),
                   formattedCount: numToSI(this.unreadInboxCount),
                 })}
@@ -121,7 +120,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 <NavLink
                   to="/reports"
                   className="p-1 nav-link border-0"
-                  title={i18n.t("unread_reports", {
+                  title={I18NextService.i18n.t("unread_reports", {
                     count: Number(this.unreadReportCount),
                     formattedCount: numToSI(this.unreadReportCount),
                   })}
@@ -141,10 +140,13 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 <NavLink
                   to="/registration_applications"
                   className="p-1 nav-link border-0"
-                  title={i18n.t("unread_registration_applications", {
-                    count: Number(this.unreadApplicationCount),
-                    formattedCount: numToSI(this.unreadApplicationCount),
-                  })}
+                  title={I18NextService.i18n.t(
+                    "unread_registration_applications",
+                    {
+                      count: Number(this.unreadApplicationCount),
+                      formattedCount: numToSI(this.unreadApplicationCount),
+                    }
+                  )}
                   onMouseUp={linkEvent(this, handleCollapseClick)}
                 >
                   <Icon icon="clipboard" />
@@ -162,7 +164,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
           className="navbar-toggler border-0 p-1"
           type="button"
           aria-label="menu"
-          data-tippy-content={i18n.t("expand_here")}
+          data-tippy-content={I18NextService.i18n.t("expand_here")}
           data-bs-toggle="collapse"
           data-bs-target="#navbarDropdown"
           aria-controls="navbarDropdown"
@@ -181,10 +183,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
               <NavLink
                 to="/communities"
                 className="nav-link"
-                title={i18n.t("communities")}
+                title={I18NextService.i18n.t("communities")}
                 onMouseUp={linkEvent(this, handleCollapseClick)}
               >
-                {i18n.t("communities")}
+                {I18NextService.i18n.t("communities")}
               </NavLink>
             </li>
             <li className="nav-item">
@@ -198,10 +200,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   state: { prevPath: this.currentLocation },
                 }}
                 className="nav-link"
-                title={i18n.t("create_post")}
+                title={I18NextService.i18n.t("create_post")}
                 onMouseUp={linkEvent(this, handleCollapseClick)}
               >
-                {i18n.t("create_post")}
+                {I18NextService.i18n.t("create_post")}
               </NavLink>
             </li>
             {this.props.siteRes && canCreateCommunity(this.props.siteRes) && (
@@ -209,22 +211,22 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 <NavLink
                   to="/create_community"
                   className="nav-link"
-                  title={i18n.t("create_community")}
+                  title={I18NextService.i18n.t("create_community")}
                   onMouseUp={linkEvent(this, handleCollapseClick)}
                 >
-                  {i18n.t("create_community")}
+                  {I18NextService.i18n.t("create_community")}
                 </NavLink>
               </li>
             )}
             <li className="nav-item">
               <a
                 className="nav-link d-inline-flex align-items-center d-md-inline-block"
-                title={i18n.t("support_lemmy")}
+                title={I18NextService.i18n.t("support_lemmy")}
                 href={donateLemmyUrl}
               >
                 <Icon icon="heart" classes="small" />
                 <span className="d-inline ms-1 d-md-none ms-md-0">
-                  {i18n.t("support_lemmy")}
+                  {I18NextService.i18n.t("support_lemmy")}
                 </span>
               </a>
             </li>
@@ -234,12 +236,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
               <NavLink
                 to="/search"
                 className="nav-link d-inline-flex align-items-center d-md-inline-block"
-                title={i18n.t("search")}
+                title={I18NextService.i18n.t("search")}
                 onMouseUp={linkEvent(this, handleCollapseClick)}
               >
                 <Icon icon="search" />
                 <span className="d-inline ms-1 d-md-none ms-md-0">
-                  {i18n.t("search")}
+                  {I18NextService.i18n.t("search")}
                 </span>
               </NavLink>
             </li>
@@ -248,12 +250,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 <NavLink
                   to="/admin"
                   className="nav-link d-inline-flex align-items-center d-md-inline-block"
-                  title={i18n.t("admin_settings")}
+                  title={I18NextService.i18n.t("admin_settings")}
                   onMouseUp={linkEvent(this, handleCollapseClick)}
                 >
                   <Icon icon="settings" />
                   <span className="d-inline ms-1 d-md-none ms-md-0">
-                    {i18n.t("admin_settings")}
+                    {I18NextService.i18n.t("admin_settings")}
                   </span>
                 </NavLink>
               </li>
@@ -264,7 +266,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   <NavLink
                     className="nav-link d-inline-flex align-items-center d-md-inline-block"
                     to="/inbox"
-                    title={i18n.t("unread_messages", {
+                    title={I18NextService.i18n.t("unread_messages", {
                       count: Number(this.unreadInboxCount),
                       formattedCount: numToSI(this.unreadInboxCount),
                     })}
@@ -272,7 +274,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   >
                     <Icon icon="bell" />
                     <span className="badge text-bg-light d-inline ms-1 d-md-none ms-md-0">
-                      {i18n.t("unread_messages", {
+                      {I18NextService.i18n.t("unread_messages", {
                         count: Number(this.unreadInboxCount),
                         formattedCount: numToSI(this.unreadInboxCount),
                       })}
@@ -289,7 +291,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     <NavLink
                       className="nav-link d-inline-flex align-items-center d-md-inline-block"
                       to="/reports"
-                      title={i18n.t("unread_reports", {
+                      title={I18NextService.i18n.t("unread_reports", {
                         count: Number(this.unreadReportCount),
                         formattedCount: numToSI(this.unreadReportCount),
                       })}
@@ -297,7 +299,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     >
                       <Icon icon="shield" />
                       <span className="badge text-bg-light d-inline ms-1 d-md-none ms-md-0">
-                        {i18n.t("unread_reports", {
+                        {I18NextService.i18n.t("unread_reports", {
                           count: Number(this.unreadReportCount),
                           formattedCount: numToSI(this.unreadReportCount),
                         })}
@@ -315,18 +317,26 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     <NavLink
                       to="/registration_applications"
                       className="nav-link d-inline-flex align-items-center d-md-inline-block"
-                      title={i18n.t("unread_registration_applications", {
-                        count: Number(this.unreadApplicationCount),
-                        formattedCount: numToSI(this.unreadApplicationCount),
-                      })}
+                      title={I18NextService.i18n.t(
+                        "unread_registration_applications",
+                        {
+                          count: Number(this.unreadApplicationCount),
+                          formattedCount: numToSI(this.unreadApplicationCount),
+                        }
+                      )}
                       onMouseUp={linkEvent(this, handleCollapseClick)}
                     >
                       <Icon icon="clipboard" />
                       <span className="badge text-bg-light d-inline ms-1 d-md-none ms-md-0">
-                        {i18n.t("unread_registration_applications", {
-                          count: Number(this.unreadApplicationCount),
-                          formattedCount: numToSI(this.unreadApplicationCount),
-                        })}
+                        {I18NextService.i18n.t(
+                          "unread_registration_applications",
+                          {
+                            count: Number(this.unreadApplicationCount),
+                            formattedCount: numToSI(
+                              this.unreadApplicationCount
+                            ),
+                          }
+                        )}
                       </span>
                       {this.unreadApplicationCount > 0 && (
                         <span className="mx-1 badge text-bg-light">
@@ -357,22 +367,22 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                         <NavLink
                           to={`/u/${person.name}`}
                           className="dropdown-item px-2"
-                          title={i18n.t("profile")}
+                          title={I18NextService.i18n.t("profile")}
                           onMouseUp={linkEvent(this, handleCollapseClick)}
                         >
                           <Icon icon="user" classes="me-1" />
-                          {i18n.t("profile")}
+                          {I18NextService.i18n.t("profile")}
                         </NavLink>
                       </li>
                       <li>
                         <NavLink
                           to="/settings"
                           className="dropdown-item px-2"
-                          title={i18n.t("settings")}
+                          title={I18NextService.i18n.t("settings")}
                           onMouseUp={linkEvent(this, handleCollapseClick)}
                         >
                           <Icon icon="settings" classes="me-1" />
-                          {i18n.t("settings")}
+                          {I18NextService.i18n.t("settings")}
                         </NavLink>
                       </li>
                       <li>
@@ -384,7 +394,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                           onClick={linkEvent(this, handleLogOut)}
                         >
                           <Icon icon="log-out" classes="me-1" />
-                          {i18n.t("logout")}
+                          {I18NextService.i18n.t("logout")}
                         </button>
                       </li>
                     </ul>
@@ -397,20 +407,20 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   <NavLink
                     to="/login"
                     className="nav-link"
-                    title={i18n.t("login")}
+                    title={I18NextService.i18n.t("login")}
                     onMouseUp={linkEvent(this, handleCollapseClick)}
                   >
-                    {i18n.t("login")}
+                    {I18NextService.i18n.t("login")}
                   </NavLink>
                 </li>
                 <li className="nav-item">
                   <NavLink
                     to="/signup"
                     className="nav-link"
-                    title={i18n.t("sign_up")}
+                    title={I18NextService.i18n.t("sign_up")}
                     onMouseUp={linkEvent(this, handleCollapseClick)}
                   >
-                    {i18n.t("sign_up")}
+                    {I18NextService.i18n.t("sign_up")}
                   </NavLink>
                 </li>
               </>
@@ -504,7 +514,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
     if (UserService.Instance.myUserInfo) {
       document.addEventListener("DOMContentLoaded", function () {
         if (!Notification) {
-          toast(i18n.t("notifications_error"), "danger");
+          toast(I18NextService.i18n.t("notifications_error"), "danger");
           return;
         }
 
index c399fb06a666ef42537b1b69618539441349ec83..294960a812d35711983c4e33728189ae74fb7e36 100644 (file)
@@ -4,9 +4,8 @@ import { Component } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Link } from "inferno-router";
 import { CreateComment, EditComment, Language } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { CommentNodeI } from "../../interfaces";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { Icon } from "../common/icon";
 import { MarkdownTextArea } from "../common/markdown-textarea";
 
@@ -58,7 +57,7 @@ export class CommentForm extends Component<CommentFormProps, any> {
             disabled={this.props.disabled}
             onSubmit={this.handleCommentSubmit}
             onReplyCancel={this.props.onReplyCancel}
-            placeholder={i18n.t("comment_here")}
+            placeholder={I18NextService.i18n.t("comment_here") ?? undefined}
             allLanguages={this.props.allLanguages}
             siteLanguages={this.props.siteLanguages}
           />
@@ -79,10 +78,10 @@ export class CommentForm extends Component<CommentFormProps, any> {
 
   get buttonTitle(): string {
     return typeof this.props.node === "number"
-      ? capitalizeFirstLetter(i18n.t("post"))
+      ? capitalizeFirstLetter(I18NextService.i18n.t("post"))
       : this.props.edit
-      ? capitalizeFirstLetter(i18n.t("save"))
-      : capitalizeFirstLetter(i18n.t("reply"));
+      ? capitalizeFirstLetter(I18NextService.i18n.t("save"))
+      : capitalizeFirstLetter(I18NextService.i18n.t("reply"));
   }
 
   handleCommentSubmit(content: string, form_id: string, language_id?: number) {
index f6cb4b221f77f16b0eb4a65d655883f88befd923..b558d142ce9f10646f8cfe30ebe84f68a90e1609 100644 (file)
@@ -48,7 +48,6 @@ import {
 } from "lemmy-js-client";
 import moment from "moment";
 import { commentTreeMaxDepth } from "../../config";
-import { i18n } from "../../i18next";
 import {
   BanType,
   CommentNodeI,
@@ -57,7 +56,7 @@ import {
   VoteType,
 } from "../../interfaces";
 import { mdToHtml, mdToHtmlNoImages } from "../../markdown";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { setupTippy } from "../../tippy";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
 import { MomentTime } from "../common/moment-time";
@@ -241,8 +240,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
     const purgeTypeText =
       this.state.purgeType == PurgeType.Comment
-        ? i18n.t("purge_comment")
-        : `${i18n.t("purge")} ${cv.creator.name}`;
+        ? I18NextService.i18n.t("purge_comment")
+        : `${I18NextService.i18n.t("purge")} ${cv.creator.name}`;
 
     const canMod_ = canMod(
       cv.creator.id,
@@ -314,27 +313,27 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               )}
               {this.isPostCreator && (
                 <div className="badge text-bg-light d-none d-sm-inline me-2">
-                  {i18n.t("creator")}
+                  {I18NextService.i18n.t("creator")}
                 </div>
               )}
               {isMod_ && (
                 <div className="badge text-bg-light d-none d-sm-inline me-2">
-                  {i18n.t("mod")}
+                  {I18NextService.i18n.t("mod")}
                 </div>
               )}
               {isAdmin_ && (
                 <div className="badge text-bg-light d-none d-sm-inline me-2">
-                  {i18n.t("admin")}
+                  {I18NextService.i18n.t("admin")}
                 </div>
               )}
               {cv.creator.bot_account && (
                 <div className="badge text-bg-light d-none d-sm-inline me-2">
-                  {i18n.t("bot_account").toLowerCase()}
+                  {I18NextService.i18n.t("bot_account").toLowerCase()}
                 </div>
               )}
               {this.props.showCommunity && (
                 <>
-                  <span className="mx-1">{i18n.t("to")}</span>
+                  <span className="mx-1">{I18NextService.i18n.t("to")}</span>
                   <CommunityLink community={cv.community} />
                   <span className="mx-2">•</span>
                   <Link className="me-2" to={`/post/${cv.post.id}`}>
@@ -366,7 +365,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                     ) : (
                       <span
                         className="me-1 font-weight-bold"
-                        aria-label={i18n.t("number_of_points", {
+                        aria-label={I18NextService.i18n.t("number_of_points", {
                           count: Number(this.commentView.counts.score),
                           formattedCount: numToSI(
                             this.commentView.counts.score
@@ -426,13 +425,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       onClick={linkEvent(this, this.handleMarkAsRead)}
                       data-tippy-content={
                         this.commentReplyOrMentionRead
-                          ? i18n.t("mark_as_unread")
-                          : i18n.t("mark_as_read")
+                          ? I18NextService.i18n.t("mark_as_unread")
+                          : I18NextService.i18n.t("mark_as_read")
                       }
                       aria-label={
                         this.commentReplyOrMentionRead
-                          ? i18n.t("mark_as_unread")
-                          : i18n.t("mark_as_read")
+                          ? I18NextService.i18n.t("mark_as_unread")
+                          : I18NextService.i18n.t("mark_as_read")
                       }
                     >
                       {this.state.readLoading ? (
@@ -456,8 +455,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                             : "text-muted"
                         }`}
                         onClick={linkEvent(this, this.handleUpvote)}
-                        data-tippy-content={i18n.t("upvote")}
-                        aria-label={i18n.t("upvote")}
+                        data-tippy-content={I18NextService.i18n.t("upvote")}
+                        aria-label={I18NextService.i18n.t("upvote")}
                         aria-pressed={this.commentView.my_vote === 1}
                       >
                         {this.state.upvoteLoading ? (
@@ -483,8 +482,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               : "text-muted"
                           }`}
                           onClick={linkEvent(this, this.handleDownvote)}
-                          data-tippy-content={i18n.t("downvote")}
-                          aria-label={i18n.t("downvote")}
+                          data-tippy-content={I18NextService.i18n.t("downvote")}
+                          aria-label={I18NextService.i18n.t("downvote")}
                           aria-pressed={this.commentView.my_vote === -1}
                         >
                           {this.state.downvoteLoading ? (
@@ -506,8 +505,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       <button
                         className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleReplyClick)}
-                        data-tippy-content={i18n.t("reply")}
-                        aria-label={i18n.t("reply")}
+                        data-tippy-content={I18NextService.i18n.t("reply")}
+                        aria-label={I18NextService.i18n.t("reply")}
                       >
                         <Icon icon="reply1" classes="icon-inline" />
                       </button>
@@ -515,8 +514,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                         <button
                           className="btn btn-link btn-animate text-muted btn-more"
                           onClick={linkEvent(this, this.handleShowAdvanced)}
-                          data-tippy-content={i18n.t("more")}
-                          aria-label={i18n.t("more")}
+                          data-tippy-content={I18NextService.i18n.t("more")}
+                          aria-label={I18NextService.i18n.t("more")}
                         >
                           <Icon icon="more-vertical" classes="icon-inline" />
                         </button>
@@ -527,7 +526,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               <Link
                                 className="btn btn-link btn-animate text-muted"
                                 to={`/create_private_message/${cv.creator.id}`}
-                                title={i18n.t("message").toLowerCase()}
+                                title={I18NextService.i18n
+                                  .t("message")
+                                  .toLowerCase()}
                               >
                                 <Icon icon="mail" />
                               </Link>
@@ -537,10 +538,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   this,
                                   this.handleShowReportDialog
                                 )}
-                                data-tippy-content={i18n.t(
+                                data-tippy-content={I18NextService.i18n.t(
+                                  "show_report_dialog"
+                                )}
+                                aria-label={I18NextService.i18n.t(
                                   "show_report_dialog"
                                 )}
-                                aria-label={i18n.t("show_report_dialog")}
                               >
                                 <Icon icon="flag" />
                               </button>
@@ -550,8 +553,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   this,
                                   this.handleBlockPerson
                                 )}
-                                data-tippy-content={i18n.t("block_user")}
-                                aria-label={i18n.t("block_user")}
+                                data-tippy-content={I18NextService.i18n.t(
+                                  "block_user"
+                                )}
+                                aria-label={I18NextService.i18n.t("block_user")}
                               >
                                 {this.state.blockPersonLoading ? (
                                   <Spinner />
@@ -565,10 +570,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                             className="btn btn-link btn-animate text-muted"
                             onClick={linkEvent(this, this.handleSaveComment)}
                             data-tippy-content={
-                              cv.saved ? i18n.t("unsave") : i18n.t("save")
+                              cv.saved
+                                ? I18NextService.i18n.t("unsave")
+                                : I18NextService.i18n.t("save")
                             }
                             aria-label={
-                              cv.saved ? i18n.t("unsave") : i18n.t("save")
+                              cv.saved
+                                ? I18NextService.i18n.t("unsave")
+                                : I18NextService.i18n.t("save")
                             }
                           >
                             {this.state.saveLoading ? (
@@ -585,8 +594,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           <button
                             className="btn btn-link btn-animate text-muted"
                             onClick={linkEvent(this, this.handleViewSource)}
-                            data-tippy-content={i18n.t("view_source")}
-                            aria-label={i18n.t("view_source")}
+                            data-tippy-content={I18NextService.i18n.t(
+                              "view_source"
+                            )}
+                            aria-label={I18NextService.i18n.t("view_source")}
                           >
                             <Icon
                               icon="file-text"
@@ -600,8 +611,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               <button
                                 className="btn btn-link btn-animate text-muted"
                                 onClick={linkEvent(this, this.handleEditClick)}
-                                data-tippy-content={i18n.t("edit")}
-                                aria-label={i18n.t("edit")}
+                                data-tippy-content={I18NextService.i18n.t(
+                                  "edit"
+                                )}
+                                aria-label={I18NextService.i18n.t("edit")}
                               >
                                 <Icon icon="edit" classes="icon-inline" />
                               </button>
@@ -613,13 +626,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                 )}
                                 data-tippy-content={
                                   !cv.comment.deleted
-                                    ? i18n.t("delete")
-                                    : i18n.t("restore")
+                                    ? I18NextService.i18n.t("delete")
+                                    : I18NextService.i18n.t("restore")
                                 }
                                 aria-label={
                                   !cv.comment.deleted
-                                    ? i18n.t("delete")
-                                    : i18n.t("restore")
+                                    ? I18NextService.i18n.t("delete")
+                                    : I18NextService.i18n.t("restore")
                                 }
                               >
                                 {this.state.deleteLoading ? (
@@ -643,13 +656,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   )}
                                   data-tippy-content={
                                     !cv.comment.distinguished
-                                      ? i18n.t("distinguish")
-                                      : i18n.t("undistinguish")
+                                      ? I18NextService.i18n.t("distinguish")
+                                      : I18NextService.i18n.t("undistinguish")
                                   }
                                   aria-label={
                                     !cv.comment.distinguished
-                                      ? i18n.t("distinguish")
-                                      : i18n.t("undistinguish")
+                                      ? I18NextService.i18n.t("distinguish")
+                                      : I18NextService.i18n.t("undistinguish")
                                   }
                                 >
                                   <Icon
@@ -672,9 +685,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     this,
                                     this.handleModRemoveShow
                                   )}
-                                  aria-label={i18n.t("remove")}
+                                  aria-label={I18NextService.i18n.t("remove")}
                                 >
-                                  {i18n.t("remove")}
+                                  {I18NextService.i18n.t("remove")}
                                 </button>
                               ) : (
                                 <button
@@ -683,12 +696,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     this,
                                     this.handleRemoveComment
                                   )}
-                                  aria-label={i18n.t("restore")}
+                                  aria-label={I18NextService.i18n.t("restore")}
                                 >
                                   {this.state.removeLoading ? (
                                     <Spinner />
                                   ) : (
-                                    i18n.t("restore")
+                                    I18NextService.i18n.t("restore")
                                   )}
                                 </button>
                               )}
@@ -705,9 +718,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                       this,
                                       this.handleModBanFromCommunityShow
                                     )}
-                                    aria-label={i18n.t("ban_from_community")}
+                                    aria-label={I18NextService.i18n.t(
+                                      "ban_from_community"
+                                    )}
                                   >
-                                    {i18n.t("ban_from_community")}
+                                    {I18NextService.i18n.t(
+                                      "ban_from_community"
+                                    )}
                                   </button>
                                 ) : (
                                   <button
@@ -716,12 +733,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                       this,
                                       this.handleBanPersonFromCommunity
                                     )}
-                                    aria-label={i18n.t("unban")}
+                                    aria-label={I18NextService.i18n.t("unban")}
                                   >
                                     {this.state.banLoading ? (
                                       <Spinner />
                                     ) : (
-                                      i18n.t("unban")
+                                      I18NextService.i18n.t("unban")
                                     )}
                                   </button>
                                 ))}
@@ -735,21 +752,25 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     )}
                                     aria-label={
                                       isMod_
-                                        ? i18n.t("remove_as_mod")
-                                        : i18n.t("appoint_as_mod")
+                                        ? I18NextService.i18n.t("remove_as_mod")
+                                        : I18NextService.i18n.t(
+                                            "appoint_as_mod"
+                                          )
                                     }
                                   >
                                     {isMod_
-                                      ? i18n.t("remove_as_mod")
-                                      : i18n.t("appoint_as_mod")}
+                                      ? I18NextService.i18n.t("remove_as_mod")
+                                      : I18NextService.i18n.t("appoint_as_mod")}
                                   </button>
                                 ) : (
                                   <>
                                     <button
                                       className="btn btn-link btn-animate text-muted"
-                                      aria-label={i18n.t("are_you_sure")}
+                                      aria-label={I18NextService.i18n.t(
+                                        "are_you_sure"
+                                      )}
                                     >
-                                      {i18n.t("are_you_sure")}
+                                      {I18NextService.i18n.t("are_you_sure")}
                                     </button>
                                     <button
                                       className="btn btn-link btn-animate text-muted"
@@ -757,12 +778,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         this,
                                         this.handleAddModToCommunity
                                       )}
-                                      aria-label={i18n.t("yes")}
+                                      aria-label={I18NextService.i18n.t("yes")}
                                     >
                                       {this.state.addModLoading ? (
                                         <Spinner />
                                       ) : (
-                                        i18n.t("yes")
+                                        I18NextService.i18n.t("yes")
                                       )}
                                     </button>
                                     <button
@@ -771,9 +792,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         this,
                                         this.handleCancelConfirmAppointAsMod
                                       )}
-                                      aria-label={i18n.t("no")}
+                                      aria-label={I18NextService.i18n.t("no")}
                                     >
-                                      {i18n.t("no")}
+                                      {I18NextService.i18n.t("no")}
                                     </button>
                                   </>
                                 ))}
@@ -790,17 +811,21 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   this,
                                   this.handleShowConfirmTransferCommunity
                                 )}
-                                aria-label={i18n.t("transfer_community")}
+                                aria-label={I18NextService.i18n.t(
+                                  "transfer_community"
+                                )}
                               >
-                                {i18n.t("transfer_community")}
+                                {I18NextService.i18n.t("transfer_community")}
                               </button>
                             ) : (
                               <>
                                 <button
                                   className="btn btn-link btn-animate text-muted"
-                                  aria-label={i18n.t("are_you_sure")}
+                                  aria-label={I18NextService.i18n.t(
+                                    "are_you_sure"
+                                  )}
                                 >
-                                  {i18n.t("are_you_sure")}
+                                  {I18NextService.i18n.t("are_you_sure")}
                                 </button>
                                 <button
                                   className="btn btn-link btn-animate text-muted"
@@ -808,12 +833,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     this,
                                     this.handleTransferCommunity
                                   )}
-                                  aria-label={i18n.t("yes")}
+                                  aria-label={I18NextService.i18n.t("yes")}
                                 >
                                   {this.state.transferCommunityLoading ? (
                                     <Spinner />
                                   ) : (
-                                    i18n.t("yes")
+                                    I18NextService.i18n.t("yes")
                                   )}
                                 </button>
                                 <button
@@ -823,9 +848,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     this
                                       .handleCancelShowConfirmTransferCommunity
                                   )}
-                                  aria-label={i18n.t("no")}
+                                  aria-label={I18NextService.i18n.t("no")}
                                 >
-                                  {i18n.t("no")}
+                                  {I18NextService.i18n.t("no")}
                                 </button>
                               </>
                             ))}
@@ -840,9 +865,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                       this,
                                       this.handlePurgePersonShow
                                     )}
-                                    aria-label={i18n.t("purge_user")}
+                                    aria-label={I18NextService.i18n.t(
+                                      "purge_user"
+                                    )}
                                   >
-                                    {i18n.t("purge_user")}
+                                    {I18NextService.i18n.t("purge_user")}
                                   </button>
                                   <button
                                     className="btn btn-link btn-animate text-muted"
@@ -850,9 +877,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                       this,
                                       this.handlePurgeCommentShow
                                     )}
-                                    aria-label={i18n.t("purge_comment")}
+                                    aria-label={I18NextService.i18n.t(
+                                      "purge_comment"
+                                    )}
                                   >
-                                    {i18n.t("purge_comment")}
+                                    {I18NextService.i18n.t("purge_comment")}
                                   </button>
 
                                   {!isBanned(cv.creator) ? (
@@ -862,9 +891,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         this,
                                         this.handleModBanShow
                                       )}
-                                      aria-label={i18n.t("ban_from_site")}
+                                      aria-label={I18NextService.i18n.t(
+                                        "ban_from_site"
+                                      )}
                                     >
-                                      {i18n.t("ban_from_site")}
+                                      {I18NextService.i18n.t("ban_from_site")}
                                     </button>
                                   ) : (
                                     <button
@@ -873,12 +904,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         this,
                                         this.handleBanPerson
                                       )}
-                                      aria-label={i18n.t("unban_from_site")}
+                                      aria-label={I18NextService.i18n.t(
+                                        "unban_from_site"
+                                      )}
                                     >
                                       {this.state.banLoading ? (
                                         <Spinner />
                                       ) : (
-                                        i18n.t("unban_from_site")
+                                        I18NextService.i18n.t("unban_from_site")
                                       )}
                                     </button>
                                   )}
@@ -895,18 +928,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     )}
                                     aria-label={
                                       isAdmin_
-                                        ? i18n.t("remove_as_admin")
-                                        : i18n.t("appoint_as_admin")
+                                        ? I18NextService.i18n.t(
+                                            "remove_as_admin"
+                                          )
+                                        : I18NextService.i18n.t(
+                                            "appoint_as_admin"
+                                          )
                                     }
                                   >
                                     {isAdmin_
-                                      ? i18n.t("remove_as_admin")
-                                      : i18n.t("appoint_as_admin")}
+                                      ? I18NextService.i18n.t("remove_as_admin")
+                                      : I18NextService.i18n.t(
+                                          "appoint_as_admin"
+                                        )}
                                   </button>
                                 ) : (
                                   <>
                                     <button className="btn btn-link btn-animate text-muted">
-                                      {i18n.t("are_you_sure")}
+                                      {I18NextService.i18n.t("are_you_sure")}
                                     </button>
                                     <button
                                       className="btn btn-link btn-animate text-muted"
@@ -914,12 +953,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         this,
                                         this.handleAddAdmin
                                       )}
-                                      aria-label={i18n.t("yes")}
+                                      aria-label={I18NextService.i18n.t("yes")}
                                     >
                                       {this.state.addAdminLoading ? (
                                         <Spinner />
                                       ) : (
-                                        i18n.t("yes")
+                                        I18NextService.i18n.t("yes")
                                       )}
                                     </button>
                                     <button
@@ -928,9 +967,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         this,
                                         this.handleCancelConfirmAppointAsAdmin
                                       )}
-                                      aria-label={i18n.t("no")}
+                                      aria-label={I18NextService.i18n.t("no")}
                                     >
-                                      {i18n.t("no")}
+                                      {I18NextService.i18n.t("no")}
                                     </button>
                                   </>
                                 ))}
@@ -961,7 +1000,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                 <Spinner />
               ) : (
                 <>
-                  {i18n.t("x_more_replies", {
+                  {I18NextService.i18n.t("x_more_replies", {
                     count: node.comment_view.counts.child_count,
                     formattedCount: numToSI(
                       node.comment_view.counts.child_count
@@ -983,22 +1022,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               className="visually-hidden"
               htmlFor={`mod-remove-reason-${cv.comment.id}`}
             >
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id={`mod-remove-reason-${cv.comment.id}`}
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               value={this.state.removeReason}
               onInput={linkEvent(this, this.handleModRemoveReasonChange)}
             />
             <button
               type="submit"
               className="btn btn-secondary"
-              aria-label={i18n.t("remove_comment")}
+              aria-label={I18NextService.i18n.t("remove_comment")}
             >
-              {i18n.t("remove_comment")}
+              {I18NextService.i18n.t("remove_comment")}
             </button>
           </form>
         )}
@@ -1011,23 +1050,23 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               className="visually-hidden"
               htmlFor={`report-reason-${cv.comment.id}`}
             >
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               required
               id={`report-reason-${cv.comment.id}`}
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               value={this.state.reportReason}
               onInput={linkEvent(this, this.handleReportReasonChange)}
             />
             <button
               type="submit"
               className="btn btn-secondary"
-              aria-label={i18n.t("create_report")}
+              aria-label={I18NextService.i18n.t("create_report")}
             >
-              {i18n.t("create_report")}
+              {I18NextService.i18n.t("create_report")}
             </button>
           </form>
         )}
@@ -1038,13 +1077,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                 className="col-form-label"
                 htmlFor={`mod-ban-reason-${cv.comment.id}`}
               >
-                {i18n.t("reason")}
+                {I18NextService.i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id={`mod-ban-reason-${cv.comment.id}`}
                 className="form-control me-2"
-                placeholder={i18n.t("reason")}
+                placeholder={I18NextService.i18n.t("reason")}
                 value={this.state.banReason}
                 onInput={linkEvent(this, this.handleModBanReasonChange)}
               />
@@ -1052,13 +1091,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                 className="col-form-label"
                 htmlFor={`mod-ban-expires-${cv.comment.id}`}
               >
-                {i18n.t("expires")}
+                {I18NextService.i18n.t("expires")}
               </label>
               <input
                 type="number"
                 id={`mod-ban-expires-${cv.comment.id}`}
                 className="form-control me-2"
-                placeholder={i18n.t("number_of_days")}
+                placeholder={I18NextService.i18n.t("number_of_days")}
                 value={this.state.banExpireDays}
                 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
               />
@@ -1074,9 +1113,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                   <label
                     className="form-check-label"
                     htmlFor="mod-ban-remove-data"
-                    title={i18n.t("remove_content_more")}
+                    title={I18NextService.i18n.t("remove_content_more")}
                   >
-                    {i18n.t("remove_content")}
+                    {I18NextService.i18n.t("remove_content")}
                   </label>
                 </div>
               </div>
@@ -1084,19 +1123,19 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             {/* TODO hold off on expires until later */}
             {/* <div class="mb-3 row"> */}
             {/*   <label class="col-form-label">Expires</label> */}
-            {/*   <input type="date" class="form-control me-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
+            {/*   <input type="date" class="form-control me-2" placeholder={I18NextService.i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
             {/* </div> */}
             <div className="mb-3 row">
               <button
                 type="submit"
                 className="btn btn-secondary"
-                aria-label={i18n.t("ban")}
+                aria-label={I18NextService.i18n.t("ban")}
               >
                 {this.state.banLoading ? (
                   <Spinner />
                 ) : (
                   <span>
-                    {i18n.t("ban")} {cv.creator.name}
+                    {I18NextService.i18n.t("ban")} {cv.creator.name}
                   </span>
                 )}
               </button>
@@ -1108,13 +1147,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
           <form onSubmit={linkEvent(this, this.handlePurgeBothSubmit)}>
             <PurgeWarning />
             <label className="visually-hidden" htmlFor="purge-reason">
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id="purge-reason"
               className="form-control my-3"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               value={this.state.purgeReason}
               onInput={linkEvent(this, this.handlePurgeReasonChange)}
             />
@@ -1209,8 +1248,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     });
 
     const title = this.props.showContext
-      ? i18n.t("show_context")
-      : i18n.t("link");
+      ? I18NextService.i18n.t("show_context")
+      : I18NextService.i18n.t("link");
 
     // The context button should show the parent comment by default
     const parentCommentId = getCommentParentId(cv.comment) ?? cv.comment.id;
@@ -1255,17 +1294,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   get pointsTippy(): string {
-    const points = i18n.t("number_of_points", {
+    const points = I18NextService.i18n.t("number_of_points", {
       count: Number(this.commentView.counts.score),
       formattedCount: numToSI(this.commentView.counts.score),
     });
 
-    const upvotes = i18n.t("number_of_upvotes", {
+    const upvotes = I18NextService.i18n.t("number_of_upvotes", {
       count: Number(this.commentView.counts.upvotes),
       formattedCount: numToSI(this.commentView.counts.upvotes),
     });
 
-    const downvotes = i18n.t("number_of_downvotes", {
+    const downvotes = I18NextService.i18n.t("number_of_downvotes", {
       count: Number(this.commentView.counts.downvotes),
       formattedCount: numToSI(this.commentView.counts.downvotes),
     });
@@ -1274,15 +1313,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   get expandText(): string {
-    return this.state.collapsed ? i18n.t("expand") : i18n.t("collapse");
+    return this.state.collapsed
+      ? I18NextService.i18n.t("expand")
+      : I18NextService.i18n.t("collapse");
   }
 
   get commentUnlessRemoved(): string {
     const comment = this.commentView.comment;
     return comment.removed
-      ? `*${i18n.t("removed")}*`
+      ? `*${I18NextService.i18n.t("removed")}*`
       : comment.deleted
-      ? `*${i18n.t("deleted")}*`
+      ? `*${I18NextService.i18n.t("deleted")}*`
       : comment.content;
   }
 
index b3630096e37cde8a4dac48d8e29e1e765b153ec4..3b328f67a28cf1103e391fe1a4800c96cb95fd82 100644 (file)
@@ -6,8 +6,8 @@ import {
   CommentView,
   ResolveCommentReport,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { CommentNodeI, CommentViewType } from "../../interfaces";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { PersonListing } from "../person/person-listing";
 import { CommentNode } from "./comment-node";
@@ -43,7 +43,7 @@ export class CommentReport extends Component<
   render() {
     const r = this.props.report;
     const comment = r.comment;
-    const tippyContent = i18n.t(
+    const tippyContent = I18NextService.i18n.t(
       r.comment_report.resolved ? "unresolve_report" : "resolve_report"
     );
 
@@ -102,10 +102,11 @@ export class CommentReport extends Component<
           onEditComment={() => Promise.resolve({ state: "empty" })}
         />
         <div>
-          {i18n.t("reporter")}: <PersonListing person={r.creator} />
+          {I18NextService.i18n.t("reporter")}:{" "}
+          <PersonListing person={r.creator} />
         </div>
         <div>
-          {i18n.t("reason")}: {r.comment_report.reason}
+          {I18NextService.i18n.t("reason")}: {r.comment_report.reason}
         </div>
         {r.resolver && (
           <div>
index ed9aecf874e9a714b3f7e7176ab2a67d60a0d6bc..c1eeed4677e4261a1ed7646aafa084b0b8a91751 100644 (file)
@@ -5,7 +5,7 @@ import {
   CommunityId,
   SiteAggregates,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 
 interface BadgesProps {
   counts: CommunityAggregates | SiteAggregates;
@@ -29,66 +29,82 @@ export const Badges = ({ counts, communityId }: BadgesProps) => {
     <ul className="badges my-1 list-inline">
       <li
         className="list-inline-item badge text-bg-secondary pointer"
-        data-tippy-content={i18n.t("active_users_in_the_last_day", {
-          count: Number(counts.users_active_day),
-          formattedCount: numToSI(counts.users_active_day),
-        })}
+        data-tippy-content={I18NextService.i18n.t(
+          "active_users_in_the_last_day",
+          {
+            count: Number(counts.users_active_day),
+            formattedCount: numToSI(counts.users_active_day),
+          }
+        )}
       >
-        {i18n.t("number_of_users", {
+        {I18NextService.i18n.t("number_of_users", {
           count: Number(counts.users_active_day),
           formattedCount: numToSI(counts.users_active_day),
         })}{" "}
-        / {i18n.t("day")}
+        / {I18NextService.i18n.t("day")}
       </li>
       <li
         className="list-inline-item badge text-bg-secondary pointer"
-        data-tippy-content={i18n.t("active_users_in_the_last_week", {
-          count: Number(counts.users_active_week),
-          formattedCount: numToSI(counts.users_active_week),
-        })}
+        data-tippy-content={I18NextService.i18n.t(
+          "active_users_in_the_last_week",
+          {
+            count: Number(counts.users_active_week),
+            formattedCount: numToSI(counts.users_active_week),
+          }
+        )}
       >
-        {i18n.t("number_of_users", {
+        {I18NextService.i18n.t("number_of_users", {
           count: Number(counts.users_active_week),
           formattedCount: numToSI(counts.users_active_week),
         })}{" "}
-        / {i18n.t("week")}
+        / {I18NextService.i18n.t("week")}
       </li>
       <li
         className="list-inline-item badge text-bg-secondary pointer"
-        data-tippy-content={i18n.t("active_users_in_the_last_month", {
-          count: Number(counts.users_active_month),
-          formattedCount: numToSI(counts.users_active_month),
-        })}
+        data-tippy-content={I18NextService.i18n.t(
+          "active_users_in_the_last_month",
+          {
+            count: Number(counts.users_active_month),
+            formattedCount: numToSI(counts.users_active_month),
+          }
+        )}
       >
-        {i18n.t("number_of_users", {
+        {I18NextService.i18n.t("number_of_users", {
           count: Number(counts.users_active_month),
           formattedCount: numToSI(counts.users_active_month),
         })}{" "}
-        / {i18n.t("month")}
+        / {I18NextService.i18n.t("month")}
       </li>
       <li
         className="list-inline-item badge text-bg-secondary pointer"
-        data-tippy-content={i18n.t("active_users_in_the_last_six_months", {
-          count: Number(counts.users_active_half_year),
-          formattedCount: numToSI(counts.users_active_half_year),
-        })}
+        data-tippy-content={I18NextService.i18n.t(
+          "active_users_in_the_last_six_months",
+          {
+            count: Number(counts.users_active_half_year),
+            formattedCount: numToSI(counts.users_active_half_year),
+          }
+        )}
       >
-        {i18n.t("number_of_users", {
+        {I18NextService.i18n.t("number_of_users", {
           count: Number(counts.users_active_half_year),
           formattedCount: numToSI(counts.users_active_half_year),
         })}{" "}
-        / {i18n.t("number_of_months", { count: 6, formattedCount: 6 })}
+        /{" "}
+        {I18NextService.i18n.t("number_of_months", {
+          count: 6,
+          formattedCount: 6,
+        })}
       </li>
       {isSiteAggregates(counts) && (
         <>
           <li className="list-inline-item badge text-bg-secondary">
-            {i18n.t("number_of_users", {
+            {I18NextService.i18n.t("number_of_users", {
               count: Number(counts.users),
               formattedCount: numToSI(counts.users),
             })}
           </li>
           <li className="list-inline-item badge text-bg-secondary">
-            {i18n.t("number_of_communities", {
+            {I18NextService.i18n.t("number_of_communities", {
               count: Number(counts.communities),
               formattedCount: numToSI(counts.communities),
             })}
@@ -97,20 +113,20 @@ export const Badges = ({ counts, communityId }: BadgesProps) => {
       )}
       {isCommunityAggregates(counts) && (
         <li className="list-inline-item badge text-bg-secondary">
-          {i18n.t("number_of_subscribers", {
+          {I18NextService.i18n.t("number_of_subscribers", {
             count: Number(counts.subscribers),
             formattedCount: numToSI(counts.subscribers),
           })}
         </li>
       )}
       <li className="list-inline-item badge text-bg-secondary">
-        {i18n.t("number_of_posts", {
+        {I18NextService.i18n.t("number_of_posts", {
           count: Number(counts.posts),
           formattedCount: numToSI(counts.posts),
         })}
       </li>
       <li className="list-inline-item badge text-bg-secondary">
-        {i18n.t("number_of_comments", {
+        {I18NextService.i18n.t("number_of_comments", {
           count: Number(counts.comments),
           formattedCount: numToSI(counts.comments),
         })}
@@ -120,7 +136,7 @@ export const Badges = ({ counts, communityId }: BadgesProps) => {
           className="badge text-bg-primary"
           to={`/modlog${communityId ? `/${communityId}` : ""}`}
         >
-          {i18n.t("modlog")}
+          {I18NextService.i18n.t("modlog")}
         </Link>
       </li>
     </ul>
index 18eaed2aa34548a707a28ab93c1ee67b3e7b439a..ad4eebfe8cdebe5d3310838fffbd33702147e878 100644 (file)
@@ -2,7 +2,7 @@ import { randomStr } from "@utils/helpers";
 import { Component, linkEvent } from "inferno";
 import { CommentSortType } from "lemmy-js-client";
 import { relTags, sortingHelpUrl } from "../../config";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon } from "./icon";
 
 interface CommentSortSelectProps {
@@ -42,21 +42,21 @@ export class CommentSortSelect extends Component<
           value={this.state.sort}
           onChange={linkEvent(this, this.handleSortChange)}
           className="sort-select form-select d-inline-block w-auto me-2 mb-2"
-          aria-label={i18n.t("sort_type")}
+          aria-label={I18NextService.i18n.t("sort_type")}
         >
           <option disabled aria-hidden="true">
-            {i18n.t("sort_type")}
+            {I18NextService.i18n.t("sort_type")}
           </option>
-          <option value={"Hot"}>{i18n.t("hot")}</option>,
-          <option value={"Top"}>{i18n.t("top")}</option>,
-          <option value={"New"}>{i18n.t("new")}</option>
-          <option value={"Old"}>{i18n.t("old")}</option>
+          <option value={"Hot"}>{I18NextService.i18n.t("hot")}</option>,
+          <option value={"Top"}>{I18NextService.i18n.t("top")}</option>,
+          <option value={"New"}>{I18NextService.i18n.t("new")}</option>
+          <option value={"Old"}>{I18NextService.i18n.t("old")}</option>
         </select>
         <a
           className="sort-select-help text-muted"
           href={sortingHelpUrl}
           rel={relTags}
-          title={i18n.t("sorting_help")}
+          title={I18NextService.i18n.t("sorting_help")}
         >
           <Icon icon="help-circle" classes="icon-inline" />
         </a>
index b61d6840ff7b4e769d806af1a34634337e4138b5..6bf0666cf320db1ac4dcf07be74c4f19172c4077 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, linkEvent } from "inferno";
-import { i18n } from "../../i18next";
 import { DataType } from "../../interfaces";
+import { I18NextService } from "../../services";
 
 interface DataTypeSelectProps {
   type_: DataType;
@@ -44,7 +44,7 @@ export class DataTypeSelect extends Component<
             checked={this.state.type_ == DataType.Post}
             onChange={linkEvent(this, this.handleTypeChange)}
           />
-          {i18n.t("posts")}
+          {I18NextService.i18n.t("posts")}
         </label>
         <label
           className={`pointer btn btn-outline-secondary ${
@@ -58,7 +58,7 @@ export class DataTypeSelect extends Component<
             checked={this.state.type_ == DataType.Comment}
             onChange={linkEvent(this, this.handleTypeChange)}
           />
-          {i18n.t("comments")}
+          {I18NextService.i18n.t("comments")}
         </label>
       </div>
     );
index 6c6037546b4c714269be3e09a5822fbe19c9b417..ba36d755f9c032755e614e492b5418ffd91e1fda 100644 (file)
@@ -1,5 +1,5 @@
 import { Component, linkEvent } from "inferno";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { EmojiMart } from "./emoji-mart";
 import { Icon } from "./icon";
 
@@ -28,8 +28,8 @@ export class EmojiPicker extends Component<EmojiPickerProps, EmojiPickerState> {
       <span className="emoji-picker">
         <button
           className="btn btn-sm text-muted"
-          data-tippy-content={i18n.t("emoji")}
-          aria-label={i18n.t("emoji")}
+          data-tippy-content={I18NextService.i18n.t("emoji")}
+          aria-label={I18NextService.i18n.t("emoji")}
           disabled={this.props.disabled}
           onClick={linkEvent(this, this.togglePicker)}
         >
index 63eb461f24e4bb7666dad304ac8983dab913ac20..5d532589efb62fe2a7c08f2f61ccfa0131a1c468 100644 (file)
@@ -1,9 +1,9 @@
+import { httpExternalPath } from "@utils/env";
 import { htmlToText } from "html-to-text";
 import { Component } from "inferno";
 import { Helmet } from "inferno-helmet";
-import { httpExternalPath } from "../../env";
-import { i18n } from "../../i18next";
 import { md } from "../../markdown";
+import { I18NextService } from "../../services";
 
 interface HtmlTagsProps {
   title: string;
@@ -21,7 +21,7 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
 
     return (
       <Helmet title={this.props.title}>
-        <html lang={i18n.resolvedLanguage} />
+        <html lang={I18NextService.i18n.resolvedLanguage} />
 
         {["title", "og:title", "twitter:title"].map(t => (
           <meta key={t} property={t} content={this.props.title} />
index e3f5ba44509808e72c01b8da305edd4234d871c0..7aedee71f30839753e8803ef8f1d55d626d60aec 100644 (file)
@@ -1,6 +1,6 @@
 import classNames from "classnames";
 import { Component } from "inferno";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 
 interface IconProps {
   icon: string;
@@ -61,7 +61,7 @@ export class PurgeWarning extends Component<any, any> {
     return (
       <div className="purge-warning mt-2 alert alert-danger" role="alert">
         <Icon icon="alert-triangle" classes="icon-inline me-2" />
-        {i18n.t("purge_warning")}
+        {I18NextService.i18n.t("purge_warning")}
       </div>
     );
   }
index 44fa5a9efff9c869e9517eef9af389d6c378af8e..5f68ea54ff5d1c2267e1515ff036a3e1f07ac060 100644 (file)
@@ -1,7 +1,6 @@
 import { randomStr } from "@utils/helpers";
 import { Component, linkEvent } from "inferno";
-import { i18n } from "../../i18next";
-import { HttpService, UserService } from "../../services";
+import { HttpService, I18NextService, UserService } from "../../services";
 import { toast } from "../../toast";
 import { Icon } from "./icon";
 
@@ -50,7 +49,7 @@ export class ImageUploadForm extends Component<
               />
               <a
                 onClick={linkEvent(this, this.handleRemoveImage)}
-                aria-label={i18n.t("remove")}
+                aria-label={I18NextService.i18n.t("remove")}
               >
                 <Icon icon="x" classes="mini-overlay" />
               </a>
index 625379e26a4c6155299cb03949acb51b95f40e0d..382eff1e33458bd2cc82de7703628c55f3f9a609 100644 (file)
@@ -3,8 +3,7 @@ import { randomStr } from "@utils/helpers";
 import classNames from "classnames";
 import { Component, linkEvent } from "inferno";
 import { Language } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services/UserService";
+import { I18NextService, UserService } from "../../services";
 import { Icon } from "./icon";
 
 interface LanguageSelectProps {
@@ -53,7 +52,7 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
       <div className="language-select">
         {this.props.multiple && this.props.showLanguageWarning && (
           <div className="alert alert-warning" role="alert">
-            {i18n.t("undetermined_language_warning")}
+            {I18NextService.i18n.t("undetermined_language_warning")}
           </div>
         )}
         <div className="mb-3 row">
@@ -64,7 +63,9 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
             )}
             htmlFor={this.id}
           >
-            {i18n.t(this.props.multiple ? "language_plural" : "language")}
+            {I18NextService.i18n.t(
+              this.props.multiple ? "language_plural" : "language"
+            )}
           </label>
           <div
             className={classNames(`col-sm-${this.props.multiple ? 9 : 10}`, {
@@ -103,13 +104,13 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
         })}
         id={this.id}
         onChange={linkEvent(this, this.handleLanguageChange)}
-        aria-label={i18n.t("language_select_placeholder")}
+        aria-label={I18NextService.i18n.t("language_select_placeholder")}
         multiple={this.props.multiple}
         disabled={this.props.disabled}
       >
         {!this.props.multiple && (
           <option selected disabled hidden>
-            {i18n.t("language_select_placeholder")}
+            {I18NextService.i18n.t("language_select_placeholder")}
           </option>
         )}
         {filteredLangs.map(l => (
index d6ed378fe05060189ddef23ff8dccbb0b12c19ab..9d0a1b9f3184a30b5379f846a34af38c0763f333 100644 (file)
@@ -1,8 +1,7 @@
 import { randomStr } from "@utils/helpers";
 import { Component, linkEvent } from "inferno";
 import { ListingType } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 
 interface ListingTypeSelectProps {
   type_: ListingType;
@@ -42,7 +41,7 @@ export class ListingTypeSelect extends Component<
       <div className="listing-type-select btn-group btn-group-toggle flex-wrap">
         {this.props.showSubscribed && (
           <label
-            title={i18n.t("subscribed_description")}
+            title={I18NextService.i18n.t("subscribed_description")}
             className={`btn btn-outline-secondary 
             ${this.state.type_ == "Subscribed" && "active"}
             ${!UserService.Instance.myUserInfo ? "disabled" : "pointer"}
@@ -57,12 +56,12 @@ export class ListingTypeSelect extends Component<
               onChange={linkEvent(this, this.handleTypeChange)}
               disabled={!UserService.Instance.myUserInfo}
             />
-            {i18n.t("subscribed")}
+            {I18NextService.i18n.t("subscribed")}
           </label>
         )}
         {this.props.showLocal && (
           <label
-            title={i18n.t("local_description")}
+            title={I18NextService.i18n.t("local_description")}
             className={`pointer btn btn-outline-secondary ${
               this.state.type_ == "Local" && "active"
             }`}
@@ -75,11 +74,11 @@ export class ListingTypeSelect extends Component<
               checked={this.state.type_ == "Local"}
               onChange={linkEvent(this, this.handleTypeChange)}
             />
-            {i18n.t("local")}
+            {I18NextService.i18n.t("local")}
           </label>
         )}
         <label
-          title={i18n.t("all_description")}
+          title={I18NextService.i18n.t("all_description")}
           className={`pointer btn btn-outline-secondary ${
             (this.state.type_ == "All" && "active") ||
             (!this.props.showLocal && this.state.type_ == "Local" && "active")
@@ -93,7 +92,7 @@ export class ListingTypeSelect extends Component<
             checked={this.state.type_ == "All"}
             onChange={linkEvent(this, this.handleTypeChange)}
           />
-          {i18n.t("all")}
+          {I18NextService.i18n.t("all")}
         </label>
       </div>
     );
index bdd42f38c35b6bf07ee6983edda3a13d1bcaf61d..97d1d1424d626f87327ea3c2f28beea78fe5fa10 100644 (file)
@@ -12,9 +12,8 @@ import {
   maxUploadImages,
   relTags,
 } from "../../config";
-import { i18n } from "../../i18next";
 import { customEmojisLookup, mdToHtml, setupTribute } from "../../markdown";
-import { HttpService, UserService } from "../../services";
+import { HttpService, I18NextService, UserService } from "../../services";
 import { setupTippy } from "../../tippy";
 import { pictrsDeleteToast, toast } from "../../toast";
 import { EmojiPicker } from "./emoji-picker";
@@ -129,7 +128,7 @@ export class MarkdownTextArea extends Component<
     // TODO add these prompts back in at some point
     // <Prompt
     //   when={!this.props.hideNavigationWarnings && this.state.content}
-    //   message={i18n.t("block_leaving")}
+    //   message={I18NextService.i18n.t("block_leaving")}
     // />
     return (
       <form
@@ -161,7 +160,7 @@ export class MarkdownTextArea extends Component<
                     className={`mb-0 ${
                       UserService.Instance.myUserInfo && "pointer"
                     }`}
-                    data-tippy-content={i18n.t("upload_image")}
+                    data-tippy-content={I18NextService.i18n.t("upload_image")}
                   >
                     {this.state.imageUploadStatus ? (
                       <Spinner />
@@ -199,7 +198,7 @@ export class MarkdownTextArea extends Component<
                 <a
                   href={markdownHelpUrl}
                   className="btn btn-sm text-muted font-weight-bold"
-                  title={i18n.t("formatting_help")}
+                  title={I18NextService.i18n.t("formatting_help")}
                   rel={relTags}
                 >
                   <Icon icon="help-circle" classes="icon-inline" />
@@ -241,15 +240,17 @@ export class MarkdownTextArea extends Component<
                       animated
                       value={this.state.imageUploadStatus.uploaded}
                       max={this.state.imageUploadStatus.total}
-                      text={i18n.t("pictures_uploded_progess", {
-                        uploaded: this.state.imageUploadStatus.uploaded,
-                        total: this.state.imageUploadStatus.total,
-                      })}
+                      text={
+                        I18NextService.i18n.t("pictures_uploded_progess", {
+                          uploaded: this.state.imageUploadStatus.uploaded,
+                          total: this.state.imageUploadStatus.total,
+                        }) ?? undefined
+                      }
                     />
                   )}
               </div>
               <label className="visually-hidden" htmlFor={this.id}>
-                {i18n.t("body")}
+                {I18NextService.i18n.t("body")}
               </label>
             </div>
           </div>
@@ -290,7 +291,7 @@ export class MarkdownTextArea extends Component<
                 className="btn btn-sm btn-secondary ms-2"
                 onClick={linkEvent(this, this.handleReplyCancel)}
               >
-                {i18n.t("cancel")}
+                {I18NextService.i18n.t("cancel")}
               </button>
             )}
             {this.state.content && (
@@ -300,7 +301,9 @@ export class MarkdownTextArea extends Component<
                 }`}
                 onClick={linkEvent(this, this.handlePreviewToggle)}
               >
-                {this.state.previewMode ? i18n.t("edit") : i18n.t("preview")}
+                {this.state.previewMode
+                  ? I18NextService.i18n.t("edit")
+                  : I18NextService.i18n.t("preview")}
               </button>
             )}
           </div>
@@ -332,8 +335,8 @@ export class MarkdownTextArea extends Component<
     return (
       <button
         className="btn btn-sm text-muted"
-        data-tippy-content={i18n.t(type)}
-        aria-label={i18n.t(type)}
+        data-tippy-content={I18NextService.i18n.t(type)}
+        aria-label={I18NextService.i18n.t(type)}
         onClick={linkEvent(this, handleClick)}
         disabled={this.isDisabled}
       >
@@ -376,7 +379,7 @@ export class MarkdownTextArea extends Component<
 
     if (files.length > maxUploadImages) {
       toast(
-        i18n.t("too_many_images_upload", {
+        I18NextService.i18n.t("too_many_images_upload", {
           count: Number(maxUploadImages),
           formattedCount: numToSI(maxUploadImages),
         }),
@@ -677,7 +680,7 @@ export class MarkdownTextArea extends Component<
 
   handleInsertSpoiler(i: MarkdownTextArea, event: any) {
     event.preventDefault();
-    const beforeChars = `\n::: spoiler ${i18n.t("spoiler")}\n`;
+    const beforeChars = `\n::: spoiler ${I18NextService.i18n.t("spoiler")}\n`;
     const afterChars = "\n:::\n";
     i.simpleSurroundBeforeAfter(beforeChars, afterChars);
   }
index 4df6c82e30e676d6c9c44eda639a488f9d4d9c6c..1857a0070c3cd133b598bc814172de2ef24f5aff 100644 (file)
@@ -1,7 +1,7 @@
 import { capitalizeFirstLetter } from "@utils/helpers";
 import { Component } from "inferno";
 import moment from "moment";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon } from "./icon";
 
 interface MomentTimeProps {
@@ -15,18 +15,18 @@ export class MomentTime extends Component<MomentTimeProps, any> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    moment.locale([...i18n.languages]);
+    moment.locale([...I18NextService.i18n.languages]);
   }
 
   createdAndModifiedTimes() {
     const updated = this.props.updated;
-    let line = `${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(
-      this.props.published
-    )}`;
+    let line = `${capitalizeFirstLetter(
+      I18NextService.i18n.t("created")
+    )}: ${this.format(this.props.published)}`;
     if (updated) {
-      line += `\n\n\n${capitalizeFirstLetter(i18n.t("modified"))} ${this.format(
-        updated
-      )}`;
+      line += `\n\n\n${capitalizeFirstLetter(
+        I18NextService.i18n.t("modified")
+      )} ${this.format(updated)}`;
     }
     return line;
   }
index f76e69574bff07433635c51a10ae3b54c3f2a7d0..81ca87947ac01498f23a75ba8d147e4422b9eb03 100644 (file)
@@ -1,5 +1,5 @@
 import { Component } from "inferno";
-import { i18n } from "../../../shared/i18next";
+import { I18NextService } from "../../services";
 
 export interface IPromptProps {
   when: boolean;
@@ -14,7 +14,7 @@ export default class NavigationPrompt extends Component<IPromptProps, any> {
     }
 
     this.unblock = this.context.router.history.block(tx => {
-      if (window.confirm(i18n.t("block_leaving"))) {
+      if (window.confirm(I18NextService.i18n.t("block_leaving") ?? undefined)) {
         this.unblock();
         tx.retry();
       }
index 1ce38054e4991adca7b22569b761e7acfe2776f8..bd4ecf8752e48bb94ee4349e5d27fac1025a3806 100644 (file)
@@ -1,5 +1,5 @@
 import { Component, linkEvent } from "inferno";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 
 interface PaginatorProps {
   page: number;
@@ -18,13 +18,13 @@ export class Paginator extends Component<PaginatorProps, any> {
           disabled={this.props.page == 1}
           onClick={linkEvent(this, this.handlePrev)}
         >
-          {i18n.t("prev")}
+          {I18NextService.i18n.t("prev")}
         </button>
         <button
           className="btn btn-secondary"
           onClick={linkEvent(this, this.handleNext)}
         >
-          {i18n.t("next")}
+          {I18NextService.i18n.t("next")}
         </button>
       </div>
     );
index 7443749094970cd427bf9ea8cc30a07349d413cc..31fb12299663f27ec9df5d43dec9cf0966783d1e 100644 (file)
@@ -22,7 +22,7 @@ export class PictrsImage extends Component<PictrsImageProps, any> {
 
   render() {
     return (
-      <picture className="pictrs-image d-inline-block overflow-hidden">
+      <picture>
         <source srcSet={this.src("webp")} type="image/webp" />
         <source srcSet={this.props.src} />
         <source srcSet={this.src("jpg")} type="image/jpeg" />
@@ -31,7 +31,7 @@ export class PictrsImage extends Component<PictrsImageProps, any> {
           alt={this.alt()}
           title={this.alt()}
           loading="lazy"
-          className={classNames({
+          className={classNames("overflow-hidden pictrs-image", {
             "img-fluid": !this.props.icon && !this.props.iconOverlay,
             banner: this.props.banner,
             "thumbnail rounded":
index 6e6914b3fb616396f79fae51191a33334e4c63e9..3857c49ed1e903067d4becf6f3ed9d1010d10ae0 100644 (file)
@@ -5,8 +5,8 @@ import {
   ApproveRegistrationApplication,
   RegistrationApplicationView,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
+import { I18NextService } from "../../services";
 import { PersonListing } from "../person/person-listing";
 import { Spinner } from "./icon";
 import { MarkdownTextArea } from "./markdown-textarea";
@@ -61,12 +61,14 @@ export class RegistrationApplication extends Component<
     return (
       <div className="registration-application">
         <div>
-          {i18n.t("applicant")}: <PersonListing person={a.creator} />
+          {I18NextService.i18n.t("applicant")}:{" "}
+          <PersonListing person={a.creator} />
         </div>
         <div>
-          {i18n.t("created")}: <MomentTime showAgo published={ra.published} />
+          {I18NextService.i18n.t("created")}:{" "}
+          <MomentTime showAgo published={ra.published} />
         </div>
-        <div>{i18n.t("answer")}:</div>
+        <div>{I18NextService.i18n.t("answer")}:</div>
         <div className="md-div" dangerouslySetInnerHTML={mdToHtml(ra.answer)} />
 
         {a.admin && (
@@ -84,7 +86,7 @@ export class RegistrationApplication extends Component<
                 </T>
                 {ra.deny_reason && (
                   <div>
-                    {i18n.t("deny_reason")}:{" "}
+                    {I18NextService.i18n.t("deny_reason")}:{" "}
                     <div
                       className="md-div d-inline-flex"
                       dangerouslySetInnerHTML={mdToHtml(ra.deny_reason)}
@@ -99,7 +101,7 @@ export class RegistrationApplication extends Component<
         {this.state.denyExpanded && (
           <div className="mb-3 row">
             <label className="col-sm-2 col-form-label">
-              {i18n.t("deny_reason")}
+              {I18NextService.i18n.t("deny_reason")}
             </label>
             <div className="col-sm-10">
               <MarkdownTextArea
@@ -116,18 +118,26 @@ export class RegistrationApplication extends Component<
           <button
             className="btn btn-secondary me-2 my-2"
             onClick={linkEvent(this, this.handleApprove)}
-            aria-label={i18n.t("approve")}
+            aria-label={I18NextService.i18n.t("approve")}
           >
-            {this.state.approveLoading ? <Spinner /> : i18n.t("approve")}
+            {this.state.approveLoading ? (
+              <Spinner />
+            ) : (
+              I18NextService.i18n.t("approve")
+            )}
           </button>
         )}
         {(!ra.admin_id || (ra.admin_id && accepted)) && (
           <button
             className="btn btn-secondary me-2"
             onClick={linkEvent(this, this.handleDeny)}
-            aria-label={i18n.t("deny")}
+            aria-label={I18NextService.i18n.t("deny")}
           >
-            {this.state.denyLoading ? <Spinner /> : i18n.t("deny")}
+            {this.state.denyLoading ? (
+              <Spinner />
+            ) : (
+              I18NextService.i18n.t("deny")
+            )}
           </button>
         )}
       </div>
index cf3a0f6290c57ff6f288aad01f64dab56e5073f1..a29fe1606e49effa78f1c53488cbf5109afe565e 100644 (file)
@@ -7,7 +7,7 @@ import {
   linkEvent,
   RefObject,
 } from "inferno";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "./icon";
 
 interface SearchableSelectProps {
@@ -113,7 +113,7 @@ export class SearchableSelect extends Component<
           ref={this.toggleButtonRef}
         >
           {loading
-            ? `${i18n.t("loading")}${loadingEllipses}`
+            ? `${I18NextService.i18n.t("loading")}${loadingEllipses}`
             : options[selectedIndex].label}
         </button>
         <div
@@ -131,7 +131,7 @@ export class SearchableSelect extends Component<
               ref={this.searchInputRef}
               onInput={linkEvent(this, handleSearch)}
               value={searchText}
-              placeholder={`${i18n.t("search")}...`}
+              placeholder={`${I18NextService.i18n.t("search")}...`}
             />
           </div>
           {!loading &&
index 546b3aec228a511adb68a1ebac636c5cb4e46d41..4d03ab5d662fcfade85a738f5a67867a43f68f03 100644 (file)
@@ -2,7 +2,7 @@ import { randomStr } from "@utils/helpers";
 import { Component, linkEvent } from "inferno";
 import { SortType } from "lemmy-js-client";
 import { relTags, sortingHelpUrl } from "../../config";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon } from "./icon";
 
 interface SortSelectProps {
@@ -41,43 +41,52 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
           value={this.state.sort}
           onChange={linkEvent(this, this.handleSortChange)}
           className="sort-select form-select d-inline-block w-auto me-2"
-          aria-label={i18n.t("sort_type")}
+          aria-label={I18NextService.i18n.t("sort_type")}
         >
           <option disabled aria-hidden="true">
-            {i18n.t("sort_type")}
+            {I18NextService.i18n.t("sort_type")}
           </option>
           {!this.props.hideHot && [
             <option key={"Hot"} value={"Hot"}>
-              {i18n.t("hot")}
+              {I18NextService.i18n.t("hot")}
             </option>,
             <option key={"Active"} value={"Active"}>
-              {i18n.t("active")}
+              {I18NextService.i18n.t("active")}
             </option>,
           ]}
-          <option value={"New"}>{i18n.t("new")}</option>
-          <option value={"Old"}>{i18n.t("old")}</option>
+          <option value={"New"}>{I18NextService.i18n.t("new")}</option>
+          <option value={"Old"}>{I18NextService.i18n.t("old")}</option>
           {!this.props.hideMostComments && [
             <option key={"MostComments"} value={"MostComments"}>
-              {i18n.t("most_comments")}
+              {I18NextService.i18n.t("most_comments")}
             </option>,
             <option key={"NewComments"} value={"NewComments"}>
-              {i18n.t("new_comments")}
+              {I18NextService.i18n.t("new_comments")}
             </option>,
           ]}
           <option disabled aria-hidden="true">
             ─────
           </option>
-          <option value={"TopDay"}>{i18n.t("top_day")}</option>
-          <option value={"TopWeek"}>{i18n.t("top_week")}</option>
-          <option value={"TopMonth"}>{i18n.t("top_month")}</option>
-          <option value={"TopYear"}>{i18n.t("top_year")}</option>
-          <option value={"TopAll"}>{i18n.t("top_all")}</option>
+          <option value={"TopHour"}>{I18NextService.i18n.t("top_hour")}</option>
+          <option value={"TopSixHour"}>
+            {I18NextService.i18n.t("top_six_hours")}
+          </option>
+          <option value={"TopTwelveHour"}>
+            {I18NextService.i18n.t("top_twelve_hours")}
+          </option>
+          <option value={"TopDay"}>{I18NextService.i18n.t("top_day")}</option>
+          <option value={"TopWeek"}>{I18NextService.i18n.t("top_week")}</option>
+          <option value={"TopMonth"}>
+            {I18NextService.i18n.t("top_month")}
+          </option>
+          <option value={"TopYear"}>{I18NextService.i18n.t("top_year")}</option>
+          <option value={"TopAll"}>{I18NextService.i18n.t("top_all")}</option>
         </select>
         <a
           className="sort-select-icon text-muted"
           href={sortingHelpUrl}
           rel={relTags}
-          title={i18n.t("sorting_help")}
+          title={I18NextService.i18n.t("sorting_help")}
         >
           <Icon icon="help-circle" classes="icon-inline" />
         </a>
index 9a4e836a46c73647d28974267648512dd3af9d0a..a84ec055568a9394a768b265f2adc1f386bdc288 100644 (file)
@@ -21,9 +21,8 @@ import {
   ListCommunitiesResponse,
   ListingType,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
@@ -86,7 +85,7 @@ export class Communities extends Component<any, CommunitiesState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("communities")} - ${
+    return `${I18NextService.i18n.t("communities")} - ${
       this.state.siteRes.site_view.site.name
     }`;
   }
@@ -103,7 +102,9 @@ export class Communities extends Component<any, CommunitiesState> {
         const { listingType, page } = this.getCommunitiesQueryParams();
         return (
           <div>
-            <h1 className="h4">{i18n.t("list_of_communities")}</h1>
+            <h1 className="h4">
+              {I18NextService.i18n.t("list_of_communities")}
+            </h1>
             <div className="row g-2 justify-content-between">
               <div className="col-auto">
                 <ListingTypeSelect
@@ -123,16 +124,19 @@ export class Communities extends Component<any, CommunitiesState> {
               >
                 <thead className="pointer">
                   <tr>
-                    <th>{i18n.t("name")}</th>
-                    <th className="text-right">{i18n.t("subscribers")}</th>
+                    <th>{I18NextService.i18n.t("name")}</th>
                     <th className="text-right">
-                      {i18n.t("users")} / {i18n.t("month")}
+                      {I18NextService.i18n.t("subscribers")}
+                    </th>
+                    <th className="text-right">
+                      {I18NextService.i18n.t("users")} /{" "}
+                      {I18NextService.i18n.t("month")}
                     </th>
                     <th className="text-right d-none d-lg-table-cell">
-                      {i18n.t("posts")}
+                      {I18NextService.i18n.t("posts")}
                     </th>
                     <th className="text-right d-none d-lg-table-cell">
-                      {i18n.t("comments")}
+                      {I18NextService.i18n.t("comments")}
                     </th>
                     <th></th>
                   </tr>
@@ -169,7 +173,7 @@ export class Communities extends Component<any, CommunitiesState> {
                                 this.handleFollow
                               )}
                             >
-                              {i18n.t("unsubscribe")}
+                              {I18NextService.i18n.t("unsubscribe")}
                             </button>
                           )}
                           {cv.subscribed === "NotSubscribed" && (
@@ -184,12 +188,12 @@ export class Communities extends Component<any, CommunitiesState> {
                                 this.handleFollow
                               )}
                             >
-                              {i18n.t("subscribe")}
+                              {I18NextService.i18n.t("subscribe")}
                             </button>
                           )}
                           {cv.subscribed === "Pending" && (
                             <div className="text-warning d-inline-block">
-                              {i18n.t("subscribe_pending")}
+                              {I18NextService.i18n.t("subscribe_pending")}
                             </div>
                           )}
                         </td>
@@ -230,7 +234,7 @@ export class Communities extends Component<any, CommunitiesState> {
             id="communities-search"
             className="form-control"
             value={this.state.searchText}
-            placeholder={`${i18n.t("search")}...`}
+            placeholder={`${I18NextService.i18n.t("search")}...`}
             onInput={linkEvent(this, this.handleSearchChange)}
             required
             minLength={3}
@@ -238,10 +242,10 @@ export class Communities extends Component<any, CommunitiesState> {
         </div>
         <div className="col-auto">
           <label className="visually-hidden" htmlFor="communities-search">
-            {i18n.t("search")}
+            {I18NextService.i18n.t("search")}
           </label>
           <button type="submit" className="btn btn-secondary">
-            <span>{i18n.t("search")}</span>
+            <span>{I18NextService.i18n.t("search")}</span>
           </button>
         </div>
       </form>
index ab19da82c87216d813e57e30ea5c1408f7f6bfae..6e2eba5ab702e59e18f319f9c8bc7bd5be854c75 100644 (file)
@@ -7,7 +7,7 @@ import {
   EditCommunity,
   Language,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { ImageUploadForm } from "../common/image-upload-form";
 import { LanguageSelect } from "../common/language-select";
@@ -107,10 +107,10 @@ export class CommunityForm extends Component<
               className="col-12 col-sm-2 col-form-label"
               htmlFor="community-name"
             >
-              {i18n.t("name")}
+              {I18NextService.i18n.t("name")}
               <span
                 className="position-absolute pointer unselectable ms-2 text-muted"
-                data-tippy-content={i18n.t("name_explain")}
+                data-tippy-content={I18NextService.i18n.t("name_explain")}
               >
                 <Icon icon="help-circle" classes="icon-inline" />
               </span>
@@ -125,7 +125,7 @@ export class CommunityForm extends Component<
                 required
                 minLength={3}
                 pattern="[a-z0-9_]+"
-                title={i18n.t("community_reqs")}
+                title={I18NextService.i18n.t("community_reqs")}
               />
             </div>
           </div>
@@ -135,10 +135,10 @@ export class CommunityForm extends Component<
             className="col-12 col-sm-2 col-form-label"
             htmlFor="community-title"
           >
-            {i18n.t("display_name")}
+            {I18NextService.i18n.t("display_name")}
             <span
               className="position-absolute pointer unselectable ms-2 text-muted"
-              data-tippy-content={i18n.t("display_name_explain")}
+              data-tippy-content={I18NextService.i18n.t("display_name_explain")}
             >
               <Icon icon="help-circle" classes="icon-inline" />
             </span>
@@ -158,11 +158,11 @@ export class CommunityForm extends Component<
         </div>
         <div className="mb-3 row">
           <label className="col-12 col-sm-2 col-form-label">
-            {i18n.t("icon")}
+            {I18NextService.i18n.t("icon")}
           </label>
           <div className="col-12 col-sm-10">
             <ImageUploadForm
-              uploadTitle={i18n.t("upload_icon")}
+              uploadTitle={I18NextService.i18n.t("upload_icon")}
               imageSrc={this.state.form.icon}
               onUpload={this.handleIconUpload}
               onRemove={this.handleIconRemove}
@@ -172,11 +172,11 @@ export class CommunityForm extends Component<
         </div>
         <div className="mb-3 row">
           <label className="col-12 col-sm-2 col-form-label">
-            {i18n.t("banner")}
+            {I18NextService.i18n.t("banner")}
           </label>
           <div className="col-12 col-sm-10">
             <ImageUploadForm
-              uploadTitle={i18n.t("upload_banner")}
+              uploadTitle={I18NextService.i18n.t("upload_banner")}
               imageSrc={this.state.form.banner}
               onUpload={this.handleBannerUpload}
               onRemove={this.handleBannerRemove}
@@ -185,12 +185,12 @@ export class CommunityForm extends Component<
         </div>
         <div className="mb-3 row">
           <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
-            {i18n.t("sidebar")}
+            {I18NextService.i18n.t("sidebar")}
           </label>
           <div className="col-12 col-sm-10">
             <MarkdownTextArea
               initialContent={this.state.form.description}
-              placeholder={i18n.t("description")}
+              placeholder={I18NextService.i18n.t("description") ?? undefined}
               onContentChange={this.handleCommunityDescriptionChange}
               hideNavigationWarnings
               allLanguages={[]}
@@ -202,7 +202,7 @@ export class CommunityForm extends Component<
         {this.props.enableNsfw && (
           <div className="mb-3 row">
             <legend className="col-form-label col-sm-2 pt-0">
-              {i18n.t("nsfw")}
+              {I18NextService.i18n.t("nsfw")}
             </legend>
             <div className="col-10">
               <div className="form-check">
@@ -219,7 +219,7 @@ export class CommunityForm extends Component<
         )}
         <div className="mb-3 row">
           <legend className="col-form-label col-6 pt-0">
-            {i18n.t("only_mods_can_post_in_community")}
+            {I18NextService.i18n.t("only_mods_can_post_in_community")}
           </legend>
           <div className="col-6">
             <div className="form-check">
@@ -254,9 +254,9 @@ export class CommunityForm extends Component<
               {this.props.loading ? (
                 <Spinner />
               ) : this.props.community_view ? (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               ) : (
-                capitalizeFirstLetter(i18n.t("create"))
+                capitalizeFirstLetter(I18NextService.i18n.t("create"))
               )}
             </button>
             {this.props.community_view && (
@@ -265,7 +265,7 @@ export class CommunityForm extends Component<
                 className="btn btn-secondary"
                 onClick={linkEvent(this, this.handleCancel)}
               >
-                {i18n.t("cancel")}
+                {I18NextService.i18n.t("cancel")}
               </button>
             )}
           </div>
index 195ff687011463f96c8899e960e1b9991dc4ea26..111b47cd9d4747be7da88e94fa729413096456ec 100644 (file)
@@ -15,7 +15,6 @@ import {
   updateCommunityBlock,
   updatePersonBlock,
 } from "@utils/app";
-import { restoreScrollPosition, saveScrollPosition } from "@utils/browser";
 import {
   getPageFromString,
   getQueryParams,
@@ -78,14 +77,12 @@ import {
   TransferCommunity,
 } from "lemmy-js-client";
 import { fetchLimit, relTags } from "../../config";
-import { i18n } from "../../i18next";
 import {
   CommentViewType,
   DataType,
   InitialFetchRequest,
 } from "../../interfaces";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { setupTippy } from "../../tippy";
 import { toast } from "../../toast";
@@ -231,10 +228,6 @@ export class Community extends Component<
     setupTippy();
   }
 
-  componentWillUnmount() {
-    saveScrollPosition(this.context);
-  }
-
   static async fetchInitialData({
     client,
     path,
@@ -331,7 +324,7 @@ export class Community extends Component<
                     className="btn btn-secondary d-inline-block mb-2 me-3"
                     onClick={linkEvent(this, this.handleShowSidebarMobile)}
                   >
-                    {i18n.t("sidebar")}{" "}
+                    {I18NextService.i18n.t("sidebar")}{" "}
                     <Icon
                       icon={
                         this.state.showSidebarMobile
@@ -611,7 +604,6 @@ export class Community extends Component<
       });
     }
 
-    restoreScrollPosition(this.context);
     setupTippy();
   }
 
@@ -758,14 +750,14 @@ export class Community extends Component<
   async handleCommentReport(form: CreateCommentReport) {
     const reportRes = await HttpService.client.createCommentReport(form);
     if (reportRes.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
   async handlePostReport(form: CreatePostReport) {
     const reportRes = await HttpService.client.createPostReport(form);
     if (reportRes.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
@@ -791,7 +783,7 @@ export class Community extends Component<
     const transferCommunityRes = await HttpService.client.transferCommunity(
       form
     );
-    toast(i18n.t("transfer_community"));
+    toast(I18NextService.i18n.t("transfer_community"));
     this.updateCommunityFull(transferCommunityRes);
   }
 
@@ -880,7 +872,7 @@ export class Community extends Component<
 
   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
     if (purgeRes.state == "success") {
-      toast(i18n.t("purge_success"));
+      toast(I18NextService.i18n.t("purge_success"));
       this.context.router.history.push(`/`);
     }
   }
index 8a3b198512c6e5e0239d75145f6f9669143bbf0f..2ce5af5a67cf01037872e9693b2e6290d34273bd 100644 (file)
@@ -4,8 +4,7 @@ import {
   CreateCommunity as CreateCommunityI,
   GetSiteResponse,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { HttpService } from "../../services/HttpService";
+import { HttpService, I18NextService } from "../../services";
 import { HtmlTags } from "../common/html-tags";
 import { CommunityForm } from "./community-form";
 
@@ -26,7 +25,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("create_community")} - ${
+    return `${I18NextService.i18n.t("create_community")} - ${
       this.state.siteRes.site_view.site.name
     }`;
   }
@@ -40,7 +39,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-            <h5>{i18n.t("create_community")}</h5>
+            <h5>{I18NextService.i18n.t("create_community")}</h5>
             <CommunityForm
               onUpsertCommunity={this.handleCommunityCreate}
               enableNsfw={enableNsfw(this.state.siteRes)}
index 8bc54c02438ff57af9ac1c8fdcd1963012e7af1a..81a5dafc7a492a5369bb7d362c2ab927ea63b2d2 100644 (file)
@@ -17,9 +17,8 @@ import {
   PurgeCommunity,
   RemoveCommunity,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { Badges } from "../common/badges";
 import { BannerIconHeader } from "../common/banner-icon-header";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
@@ -187,7 +186,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
               ) : (
                 <>
                   <Icon icon="check" classes="icon-inline text-success me-1" />
-                  {i18n.t("joined")}
+                  {I18NextService.i18n.t("joined")}
                 </>
               )}
             </button>
@@ -200,23 +199,23 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
               {this.state.followCommunityLoading ? (
                 <Spinner />
               ) : (
-                i18n.t("subscribe_pending")
+                I18NextService.i18n.t("subscribe_pending")
               )}
             </button>
           )}
           {community.removed && (
             <small className="me-2 text-muted font-italic">
-              {i18n.t("removed")}
+              {I18NextService.i18n.t("removed")}
             </small>
           )}
           {community.deleted && (
             <small className="me-2 text-muted font-italic">
-              {i18n.t("deleted")}
+              {I18NextService.i18n.t("deleted")}
             </small>
           )}
           {community.nsfw && (
             <small className="me-2 text-muted font-italic">
-              {i18n.t("nsfw")}
+              {I18NextService.i18n.t("nsfw")}
             </small>
           )}
         </h5>
@@ -234,7 +233,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   mods() {
     return (
       <ul className="list-inline small">
-        <li className="list-inline-item">{i18n.t("mods")}: </li>
+        <li className="list-inline-item">{I18NextService.i18n.t("mods")}: </li>
         {this.props.moderators.map(mod => (
           <li key={mod.moderator.id} className="list-inline-item">
             <PersonListing person={mod.moderator} />
@@ -253,7 +252,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         }`}
         to={`/create_post?communityId=${cv.community.id}`}
       >
-        {i18n.t("create_a_post")}
+        {I18NextService.i18n.t("create_a_post")}
       </Link>
     );
   }
@@ -270,7 +269,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             {this.state.followCommunityLoading ? (
               <Spinner />
             ) : (
-              i18n.t("subscribe")
+              I18NextService.i18n.t("subscribe")
             )}
           </button>
         )}
@@ -288,7 +287,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             className="btn btn-danger d-block mb-2 w-100"
             onClick={linkEvent(this, this.handleBlockCommunity)}
           >
-            {i18n.t(blocked ? "unblock_community" : "block_community")}
+            {I18NextService.i18n.t(
+              blocked ? "unblock_community" : "block_community"
+            )}
           </button>
         )}
       </>
@@ -315,8 +316,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                 <button
                   className="btn btn-link text-muted d-inline-block"
                   onClick={linkEvent(this, this.handleEditClick)}
-                  data-tippy-content={i18n.t("edit")}
-                  aria-label={i18n.t("edit")}
+                  data-tippy-content={I18NextService.i18n.t("edit")}
+                  aria-label={I18NextService.i18n.t("edit")}
                 >
                   <Icon icon="edit" classes="icon-inline" />
                 </button>
@@ -331,20 +332,20 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                         this.handleShowConfirmLeaveModTeamClick
                       )}
                     >
-                      {i18n.t("leave_mod_team")}
+                      {I18NextService.i18n.t("leave_mod_team")}
                     </button>
                   </li>
                 ) : (
                   <>
                     <li className="list-inline-item-action">
-                      {i18n.t("are_you_sure")}
+                      {I18NextService.i18n.t("are_you_sure")}
                     </li>
                     <li className="list-inline-item-action">
                       <button
                         className="btn btn-link text-muted d-inline-block"
                         onClick={linkEvent(this, this.handleLeaveModTeam)}
                       >
-                        {i18n.t("yes")}
+                        {I18NextService.i18n.t("yes")}
                       </button>
                     </li>
                     <li className="list-inline-item-action">
@@ -355,7 +356,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                           this.handleCancelLeaveModTeamClick
                         )}
                       >
-                        {i18n.t("no")}
+                        {I18NextService.i18n.t("no")}
                       </button>
                     </li>
                   </>
@@ -367,13 +368,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                     onClick={linkEvent(this, this.handleDeleteCommunity)}
                     data-tippy-content={
                       !community_view.community.deleted
-                        ? i18n.t("delete")
-                        : i18n.t("restore")
+                        ? I18NextService.i18n.t("delete")
+                        : I18NextService.i18n.t("restore")
                     }
                     aria-label={
                       !community_view.community.deleted
-                        ? i18n.t("delete")
-                        : i18n.t("restore")
+                        ? I18NextService.i18n.t("delete")
+                        : I18NextService.i18n.t("restore")
                     }
                   >
                     {this.state.deleteCommunityLoading ? (
@@ -398,7 +399,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                   className="btn btn-link text-muted d-inline-block"
                   onClick={linkEvent(this, this.handleModRemoveShow)}
                 >
-                  {i18n.t("remove")}
+                  {I18NextService.i18n.t("remove")}
                 </button>
               ) : (
                 <button
@@ -408,16 +409,16 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                   {this.state.removeCommunityLoading ? (
                     <Spinner />
                   ) : (
-                    i18n.t("restore")
+                    I18NextService.i18n.t("restore")
                   )}
                 </button>
               )}
               <button
                 className="btn btn-link text-muted d-inline-block"
                 onClick={linkEvent(this, this.handlePurgeCommunityShow)}
-                aria-label={i18n.t("purge_community")}
+                aria-label={I18NextService.i18n.t("purge_community")}
               >
-                {i18n.t("purge_community")}
+                {I18NextService.i18n.t("purge_community")}
               </button>
             </li>
           )}
@@ -426,13 +427,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
           <form onSubmit={linkEvent(this, this.handleRemoveCommunity)}>
             <div className="input-group mb-3">
               <label className="col-form-label" htmlFor="remove-reason">
-                {i18n.t("reason")}
+                {I18NextService.i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id="remove-reason"
                 className="form-control me-2"
-                placeholder={i18n.t("optional")}
+                placeholder={I18NextService.i18n.t("optional")}
                 value={this.state.removeReason}
                 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
               />
@@ -440,14 +441,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             {/* TODO hold off on expires for now */}
             {/* <div class="mb-3 row"> */}
             {/*   <label class="col-form-label">Expires</label> */}
-            {/*   <input type="date" class="form-control me-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
+            {/*   <input type="date" class="form-control me-2" placeholder={I18NextService.i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
             {/* </div> */}
             <div className="input-group mb-3">
               <button type="submit" className="btn btn-secondary">
                 {this.state.removeCommunityLoading ? (
                   <Spinner />
                 ) : (
-                  i18n.t("remove_community")
+                  I18NextService.i18n.t("remove_community")
                 )}
               </button>
             </div>
@@ -460,13 +461,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             </div>
             <div className="input-group mb-3">
               <label className="visually-hidden" htmlFor="purge-reason">
-                {i18n.t("reason")}
+                {I18NextService.i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id="purge-reason"
                 className="form-control me-2"
-                placeholder={i18n.t("reason")}
+                placeholder={I18NextService.i18n.t("reason")}
                 value={this.state.purgeReason}
                 onInput={linkEvent(this, this.handlePurgeReasonChange)}
               />
@@ -478,9 +479,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                 <button
                   type="submit"
                   className="btn btn-secondary"
-                  aria-label={i18n.t("purge_community")}
+                  aria-label={I18NextService.i18n.t("purge_community")}
                 >
-                  {i18n.t("purge_community")}
+                  {I18NextService.i18n.t("purge_community")}
                 </button>
               )}
             </div>
index 9b14310ce3e2f6cdd3b146aafb86aa5bcb5f903b..7ac69fedec5227662f03c0683116cafe47c1e864 100644 (file)
@@ -18,10 +18,9 @@ import {
   GetSiteResponse,
   PersonView,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
 import { removeFromEmojiDataModel, updateEmojiDataModel } from "../../markdown";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { HtmlTags } from "../common/html-tags";
@@ -108,7 +107,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("admin_settings")} - ${
+    return `${I18NextService.i18n.t("admin_settings")} - ${
       this.state.siteRes.site_view.site.name
     }`;
   }
@@ -129,7 +128,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
           tabs={[
             {
               key: "site",
-              label: i18n.t("site"),
+              label: I18NextService.i18n.t("site"),
               getNode: isSelected => (
                 <div
                   className={classNames("tab-pane show", {
@@ -181,7 +180,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
             },
             {
               key: "taglines",
-              label: i18n.t("taglines"),
+              label: I18NextService.i18n.t("taglines"),
               getNode: isSelected => (
                 <div
                   className={classNames("tab-pane", {
@@ -202,7 +201,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
             },
             {
               key: "emojis",
-              label: i18n.t("emojis"),
+              label: I18NextService.i18n.t("emojis"),
               getNode: isSelected => (
                 <div
                   className={classNames("tab-pane", {
@@ -253,7 +252,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   admins() {
     return (
       <>
-        <h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
+        <h5>{capitalizeFirstLetter(I18NextService.i18n.t("admins"))}</h5>
         <ul className="list-unstyled">
           {this.state.siteRes.admins.map(admin => (
             <li key={admin.person.id} className="list-inline-item">
@@ -275,7 +274,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         {this.state.leaveAdminTeamRes.state == "loading" ? (
           <Spinner />
         ) : (
-          i18n.t("leave_admin_team")
+          I18NextService.i18n.t("leave_admin_team")
         )}
       </button>
     );
@@ -293,7 +292,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         const bans = this.state.bannedRes.data.banned;
         return (
           <>
-            <h5>{i18n.t("banned_users")}</h5>
+            <h5>{I18NextService.i18n.t("banned_users")}</h5>
             <ul className="list-unstyled">
               {bans.map(banned => (
                 <li key={banned.person.id} className="list-inline-item">
@@ -319,7 +318,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         s.siteRes.taglines = editRes.data.taglines;
         return s;
       });
-      toast(i18n.t("site_saved"));
+      toast(I18NextService.i18n.t("site_saved"));
     }
 
     this.setState({ loading: false });
@@ -340,7 +339,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     });
 
     if (this.state.leaveAdminTeamRes.state === "success") {
-      toast(i18n.t("left_admin_team"));
+      toast(I18NextService.i18n.t("left_admin_team"));
       this.context.router.history.replace("/");
     }
   }
index 569abd0407df2891fa855ec1f8192d30b75e28aa..8428a54fe3454fa257ba7bfafaa7ad2af91cf821 100644 (file)
@@ -6,9 +6,8 @@ import {
   EditCustomEmoji,
   GetSiteResponse,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { customEmojisLookup } from "../../markdown";
-import { HttpService } from "../../services/HttpService";
+import { HttpService, I18NextService } from "../../services";
 import { pictrsDeleteToast, toast } from "../../toast";
 import { EmojiMart } from "../common/emoji-mart";
 import { HtmlTags } from "../common/html-tags";
@@ -66,7 +65,7 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
     this.handleEmojiClick = this.handleEmojiClick.bind(this);
   }
   get documentTitle(): string {
-    return i18n.t("custom_emojis");
+    return I18NextService.i18n.t("custom_emojis");
   }
 
   render() {
@@ -76,7 +75,7 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
           title={this.documentTitle}
           path={this.context.router.route.match.url}
         />
-        <h5 className="col-12">{i18n.t("custom_emojis")}</h5>
+        <h5 className="col-12">{I18NextService.i18n.t("custom_emojis")}</h5>
         {customEmojisLookup.size > 0 && (
           <div>
             <EmojiMart
@@ -89,15 +88,21 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
           <table id="emojis_table" className="table table-sm table-hover">
             <thead className="pointer">
               <tr>
-                <th>{i18n.t("column_emoji")}</th>
-                <th className="text-right">{i18n.t("column_shortcode")}</th>
-                <th className="text-right">{i18n.t("column_category")}</th>
+                <th>{I18NextService.i18n.t("column_emoji")}</th>
+                <th className="text-right">
+                  {I18NextService.i18n.t("column_shortcode")}
+                </th>
+                <th className="text-right">
+                  {I18NextService.i18n.t("column_category")}
+                </th>
                 <th className="text-right d-lg-table-cell d-none">
-                  {i18n.t("column_imageurl")}
+                  {I18NextService.i18n.t("column_imageurl")}
+                </th>
+                <th className="text-right">
+                  {I18NextService.i18n.t("column_alttext")}
                 </th>
-                <th className="text-right">{i18n.t("column_alttext")}</th>
                 <th className="text-right d-lg-table-cell">
-                  {i18n.t("column_keywords")}
+                  {I18NextService.i18n.t("column_keywords")}
                 </th>
                 <th style="width:121px"></th>
               </tr>
@@ -215,8 +220,8 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
                               { i: this, cv: cv },
                               this.handleEditEmojiClick
                             )}
-                            data-tippy-content={i18n.t("save")}
-                            aria-label={i18n.t("save")}
+                            data-tippy-content={I18NextService.i18n.t("save")}
+                            aria-label={I18NextService.i18n.t("save")}
                             disabled={
                               this.props.loading ||
                               !this.canEdit(cv) ||
@@ -236,10 +241,10 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
                             { i: this, index: index, cv: cv },
                             this.handleDeleteEmojiClick
                           )}
-                          data-tippy-content={i18n.t("delete")}
-                          aria-label={i18n.t("delete")}
+                          data-tippy-content={I18NextService.i18n.t("delete")}
+                          aria-label={I18NextService.i18n.t("delete")}
                           disabled={this.props.loading}
-                          title={i18n.t("delete")}
+                          title={I18NextService.i18n.t("delete")}
                         >
                           <Icon
                             icon="trash"
@@ -257,7 +262,7 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
             className="btn btn-sm btn-secondary me-2"
             onClick={linkEvent(this, this.handleAddEmojiClick)}
           >
-            {i18n.t("add_custom_emoji")}
+            {I18NextService.i18n.t("add_custom_emoji")}
           </button>
 
           <Paginator page={this.state.page} onChange={this.handlePageChange} />
@@ -280,8 +285,8 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
   }
 
   getEditTooltip(cv: CustomEmojiViewForm) {
-    if (this.canEdit(cv)) return i18n.t("save");
-    else return i18n.t("custom_emoji_save_validation");
+    if (this.canEdit(cv)) return I18NextService.i18n.t("save");
+    else return I18NextService.i18n.t("custom_emoji_save_validation");
   }
 
   handlePageChange(page: number) {
index a8441380aeab5d21f98a5c9c3fd516be2a0c4440..4a84664bf08d2c0961e409dcc5c04ba443a02ac0 100644 (file)
@@ -13,7 +13,6 @@ import {
   showLocal,
   updatePersonBlock,
 } from "@utils/app";
-import { restoreScrollPosition, saveScrollPosition } from "@utils/browser";
 import {
   getPageFromString,
   getQueryParams,
@@ -73,15 +72,13 @@ import {
   TransferCommunity,
 } from "lemmy-js-client";
 import { fetchLimit, relTags, trendingFetchLimit } from "../../config";
-import { i18n } from "../../i18next";
 import {
   CommentViewType,
   DataType,
   InitialFetchRequest,
 } from "../../interfaces";
 import { mdToHtml } from "../../markdown";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { setupTippy } from "../../tippy";
 import { toast } from "../../toast";
@@ -197,7 +194,7 @@ const MobileButton = ({
     className="btn btn-secondary d-inline-block mb-2 me-3"
     onClick={onClick}
   >
-    {i18n.t(textKey)}{" "}
+    {I18NextService.i18n.t(textKey)}{" "}
     <Icon icon={show ? `minus-square` : `plus-square`} classes="icon-inline" />
   </button>
 );
@@ -210,7 +207,7 @@ const LinkButton = ({
   translationKey: NoOptionI18nKeys;
 }) => (
   <Link className="btn btn-secondary d-block" to={path}>
-    {i18n.t(translationKey)}
+    {I18NextService.i18n.t(translationKey)}
   </Link>
 );
 
@@ -295,10 +292,6 @@ export class Home extends Component<any, HomeState> {
     setupTippy();
   }
 
-  componentWillUnmount() {
-    saveScrollPosition(this.context);
-  }
-
   static async fetchInitialData({
     client,
     auth,
@@ -565,10 +558,14 @@ export class Home extends Component<any, HomeState> {
               className="btn btn-sm text-muted"
               onClick={linkEvent(this, this.handleCollapseSubscribe)}
               aria-label={
-                subscribedCollapsed ? i18n.t("expand") : i18n.t("collapse")
+                subscribedCollapsed
+                  ? I18NextService.i18n.t("expand")
+                  : I18NextService.i18n.t("collapse")
               }
               data-tippy-content={
-                subscribedCollapsed ? i18n.t("expand") : i18n.t("collapse")
+                subscribedCollapsed
+                  ? I18NextService.i18n.t("expand")
+                  : I18NextService.i18n.t("collapse")
               }
               aria-expanded="true"
               aria-controls="sidebarSubscribedBody"
@@ -798,7 +795,6 @@ export class Home extends Component<any, HomeState> {
       });
     }
 
-    restoreScrollPosition(this.context);
     setupTippy();
   }
 
@@ -932,14 +928,14 @@ export class Home extends Component<any, HomeState> {
   async handleCommentReport(form: CreateCommentReport) {
     const reportRes = await HttpService.client.createCommentReport(form);
     if (reportRes.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
   async handlePostReport(form: CreatePostReport) {
     const reportRes = await HttpService.client.createPostReport(form);
     if (reportRes.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
@@ -963,7 +959,7 @@ export class Home extends Component<any, HomeState> {
 
   async handleTransferCommunity(form: TransferCommunity) {
     await HttpService.client.transferCommunity(form);
-    toast(i18n.t("transfer_community"));
+    toast(I18NextService.i18n.t("transfer_community"));
   }
 
   async handleCommentReplyRead(form: MarkCommentReplyAsRead) {
@@ -1030,7 +1026,7 @@ export class Home extends Component<any, HomeState> {
 
   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
     if (purgeRes.state == "success") {
-      toast(i18n.t("purge_success"));
+      toast(I18NextService.i18n.t("purge_success"));
       this.context.router.history.push(`/`);
     }
   }
index aba71099323ef4983f4e9ea843cbb824183cf899..b54c96af332dd045f7ab142e46c881a82e6123c9 100644 (file)
@@ -7,9 +7,8 @@ import {
   Instance,
 } from "lemmy-js-client";
 import { relTags } from "../../config";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
@@ -70,7 +69,9 @@ export class Instances extends Component<any, InstancesState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("instances")} - ${this.state.siteRes.site_view.site.name}`;
+    return `${I18NextService.i18n.t("instances")} - ${
+      this.state.siteRes.site_view.site.name
+    }`;
   }
 
   renderInstances() {
@@ -86,18 +87,18 @@ export class Instances extends Component<any, InstancesState> {
         return instances ? (
           <div className="row">
             <div className="col-md-6">
-              <h5>{i18n.t("linked_instances")}</h5>
+              <h5>{I18NextService.i18n.t("linked_instances")}</h5>
               {this.itemList(instances.linked)}
             </div>
             {instances.allowed && instances.allowed.length > 0 && (
               <div className="col-md-6">
-                <h5>{i18n.t("allowed_instances")}</h5>
+                <h5>{I18NextService.i18n.t("allowed_instances")}</h5>
                 {this.itemList(instances.allowed)}
               </div>
             )}
             {instances.blocked && instances.blocked.length > 0 && (
               <div className="col-md-6">
-                <h5>{i18n.t("blocked_instances")}</h5>
+                <h5>{I18NextService.i18n.t("blocked_instances")}</h5>
                 {this.itemList(instances.blocked)}
               </div>
             )}
@@ -127,9 +128,9 @@ export class Instances extends Component<any, InstancesState> {
         <table id="instances_table" className="table table-sm table-hover">
           <thead className="pointer">
             <tr>
-              <th>{i18n.t("name")}</th>
-              <th>{i18n.t("software")}</th>
-              <th>{i18n.t("version")}</th>
+              <th>{I18NextService.i18n.t("name")}</th>
+              <th>{I18NextService.i18n.t("software")}</th>
+              <th>{I18NextService.i18n.t("version")}</th>
             </tr>
           </thead>
           <tbody>
@@ -148,7 +149,7 @@ export class Instances extends Component<any, InstancesState> {
         </table>
       </div>
     ) : (
-      <div>{i18n.t("none_found")}</div>
+      <div>{I18NextService.i18n.t("none_found")}</div>
     );
   }
 }
index 90c461a8d94d8848edec76cb64dab979e6e4a33f..85a413ebb9d4a5edecbbde00021dd079196a12a2 100644 (file)
@@ -1,8 +1,8 @@
 import { setIsoData } from "@utils/app";
 import { Component } from "inferno";
 import { GetSiteResponse } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
+import { I18NextService } from "../../services";
 import { HtmlTags } from "../common/html-tags";
 
 interface LegalState {
@@ -20,7 +20,7 @@ export class Legal extends Component<any, LegalState> {
   }
 
   get documentTitle(): string {
-    return i18n.t("legal_information");
+    return I18NextService.i18n.t("legal_information");
   }
 
   render() {
index 3d602f916ddac3c4bed94d9e74d1eae84a1b0b61..1853a82b92d3c1b096ab7265c126d50a5a40e36e 100644 (file)
@@ -3,8 +3,7 @@ import { isBrowser } from "@utils/browser";
 import { validEmail } from "@utils/helpers";
 import { Component, linkEvent } from "inferno";
 import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { HtmlTags } from "../common/html-tags";
@@ -43,7 +42,9 @@ export class Login extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("login")} - ${this.state.siteRes.site_view.site.name}`;
+    return `${I18NextService.i18n.t("login")} - ${
+      this.state.siteRes.site_view.site.name
+    }`;
   }
 
   get isLemmyMl(): boolean {
@@ -68,13 +69,13 @@ export class Login extends Component<any, State> {
     return (
       <div>
         <form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
-          <h5>{i18n.t("login")}</h5>
+          <h5>{I18NextService.i18n.t("login")}</h5>
           <div className="mb-3 row">
             <label
               className="col-sm-2 col-form-label"
               htmlFor="login-email-or-username"
             >
-              {i18n.t("email_or_username")}
+              {I18NextService.i18n.t("email_or_username")}
             </label>
             <div className="col-sm-10">
               <input
@@ -91,7 +92,7 @@ export class Login extends Component<any, State> {
           </div>
           <div className="mb-3 row">
             <label className="col-sm-2 col-form-label" htmlFor="login-password">
-              {i18n.t("password")}
+              {I18NextService.i18n.t("password")}
             </label>
             <div className="col-sm-10">
               <input
@@ -112,9 +113,9 @@ export class Login extends Component<any, State> {
                   !!this.state.form.username_or_email &&
                   !validEmail(this.state.form.username_or_email)
                 }
-                title={i18n.t("no_password_reset")}
+                title={I18NextService.i18n.t("no_password_reset")}
               >
-                {i18n.t("forgot_password")}
+                {I18NextService.i18n.t("forgot_password")}
               </button>
             </div>
           </div>
@@ -124,7 +125,7 @@ export class Login extends Component<any, State> {
                 className="col-sm-6 col-form-label"
                 htmlFor="login-totp-token"
               >
-                {i18n.t("two_factor_token")}
+                {I18NextService.i18n.t("two_factor_token")}
               </label>
               <div className="col-sm-6">
                 <input
@@ -146,7 +147,7 @@ export class Login extends Component<any, State> {
                 {this.state.loginRes.state == "loading" ? (
                   <Spinner />
                 ) : (
-                  i18n.t("login")
+                  I18NextService.i18n.t("login")
                 )}
               </button>
             </div>
@@ -172,7 +173,7 @@ export class Login extends Component<any, State> {
         case "failed": {
           if (loginRes.msg === "missing_totp_token") {
             i.setState({ showTotp: true });
-            toast(i18n.t("enter_two_factor_code"), "info");
+            toast(I18NextService.i18n.t("enter_two_factor_code"), "info");
           }
 
           i.setState({ loginRes: { state: "failed", msg: loginRes.msg } });
@@ -220,7 +221,7 @@ export class Login extends Component<any, State> {
     if (email) {
       const res = await HttpService.client.passwordReset({ email });
       if (res.state == "success") {
-        toast(i18n.t("reset_password_mail_sent"));
+        toast(I18NextService.i18n.t("reset_password_mail_sent"));
       }
     }
   }
index 619e70d8dd501b2adfce1616e2364d6c561b1f13..9003962a93fed223fdfd35d640200a0061e705ad 100644 (file)
@@ -3,7 +3,7 @@ import { capitalizeFirstLetter } from "@utils/helpers";
 import classNames from "classnames";
 import { Component, FormEventHandler, linkEvent } from "inferno";
 import { EditSite, LocalSiteRateLimit } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Spinner } from "../common/icon";
 import Tabs from "../common/tabs";
 
@@ -57,7 +57,9 @@ function RateLimits({
   return (
     <div role="tabpanel" className={classNames("mb-3 row", className)}>
       <div className="col-md-6">
-        <label htmlFor="rate-limit">{i18n.t("rate_limit")}</label>
+        <label htmlFor="rate-limit">
+          {I18NextService.i18n.t("rate_limit")}
+        </label>
         <input
           type="number"
           id="rate-limit"
@@ -68,7 +70,9 @@ function RateLimits({
         />
       </div>
       <div className="col-md-6">
-        <label htmlFor="rate-limit-per-second">{i18n.t("per_second")}</label>
+        <label htmlFor="rate-limit-per-second">
+          {I18NextService.i18n.t("per_second")}
+        </label>
         <input
           type="number"
           id="rate-limit-per-second"
@@ -141,11 +145,11 @@ export default class RateLimitsForm extends Component<
         className="rate-limit-form"
         onSubmit={linkEvent(this, submitRateLimitForm)}
       >
-        <h5>{i18n.t("rate_limit_header")}</h5>
+        <h5>{I18NextService.i18n.t("rate_limit_header")}</h5>
         <Tabs
           tabs={rateLimitTypes.map(rateLimitType => ({
             key: rateLimitType,
-            label: i18n.t(`rate_limit_${rateLimitType}`),
+            label: I18NextService.i18n.t(`rate_limit_${rateLimitType}`),
             getNode: isSelected => (
               <RateLimits
                 className={classNames("tab-pane show", {
@@ -176,7 +180,7 @@ export default class RateLimitsForm extends Component<
             {this.props.loading ? (
               <Spinner />
             ) : (
-              capitalizeFirstLetter(i18n.t("save"))
+              capitalizeFirstLetter(I18NextService.i18n.t("save"))
             )}
           </button>
         </div>
index b595e14d65f3975617af42d2d792976eec624034..bed1262038eb2a098724e07405c0ece852e1a520 100644 (file)
@@ -7,8 +7,7 @@ import {
   LoginResponse,
   Register,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { Spinner } from "../common/icon";
 import { SiteForm } from "./site-form";
@@ -55,7 +54,7 @@ export class Setup extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("setup")} - Lemmy`;
+    return `${I18NextService.i18n.t("setup")} - Lemmy`;
   }
 
   render() {
@@ -64,7 +63,7 @@ export class Setup extends Component<any, State> {
         <Helmet title={this.documentTitle} />
         <div className="row">
           <div className="col-12 offset-lg-3 col-lg-6">
-            <h3>{i18n.t("lemmy_instance_setup")}</h3>
+            <h3>{I18NextService.i18n.t("lemmy_instance_setup")}</h3>
             {!this.state.doneRegisteringUser ? (
               this.registerUser()
             ) : (
@@ -85,10 +84,10 @@ export class Setup extends Component<any, State> {
   registerUser() {
     return (
       <form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
-        <h5>{i18n.t("setup_admin")}</h5>
+        <h5>{I18NextService.i18n.t("setup_admin")}</h5>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="username">
-            {i18n.t("username")}
+            {I18NextService.i18n.t("username")}
           </label>
           <div className="col-sm-10">
             <input
@@ -105,7 +104,7 @@ export class Setup extends Component<any, State> {
         </div>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="email">
-            {i18n.t("email")}
+            {I18NextService.i18n.t("email")}
           </label>
 
           <div className="col-sm-10">
@@ -113,7 +112,7 @@ export class Setup extends Component<any, State> {
               type="email"
               id="email"
               className="form-control"
-              placeholder={i18n.t("optional")}
+              placeholder={I18NextService.i18n.t("optional")}
               value={this.state.form.email}
               onInput={linkEvent(this, this.handleRegisterEmailChange)}
               minLength={3}
@@ -122,7 +121,7 @@ export class Setup extends Component<any, State> {
         </div>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="password">
-            {i18n.t("password")}
+            {I18NextService.i18n.t("password")}
           </label>
           <div className="col-sm-10">
             <input
@@ -140,7 +139,7 @@ export class Setup extends Component<any, State> {
         </div>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="verify-password">
-            {i18n.t("verify_password")}
+            {I18NextService.i18n.t("verify_password")}
           </label>
           <div className="col-sm-10">
             <input
@@ -162,7 +161,7 @@ export class Setup extends Component<any, State> {
               {this.state.registerRes.state == "loading" ? (
                 <Spinner />
               ) : (
-                i18n.t("sign_up")
+                I18NextService.i18n.t("sign_up")
               )}
             </button>
           </div>
index 817dcf8c888a8a5a9d9602c5ff5b1421639d8133..a2d960dc675a5ba10f973e44e1673e9c5d73cb83 100644 (file)
@@ -13,9 +13,8 @@ import {
   SiteView,
 } from "lemmy-js-client";
 import { joinLemmyUrl } from "../../config";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { HtmlTags } from "../common/html-tags";
@@ -113,7 +112,7 @@ export class Signup extends Component<any, State> {
   }
 
   titleName(siteView: SiteView): string {
-    return i18n.t(
+    return I18NextService.i18n.t(
       siteView.local_site.private_instance ? "apply_to_join" : "sign_up"
     );
   }
@@ -159,7 +158,7 @@ export class Signup extends Component<any, State> {
             className="col-sm-2 col-form-label"
             htmlFor="register-username"
           >
-            {i18n.t("username")}
+            {I18NextService.i18n.t("username")}
           </label>
 
           <div className="col-sm-10">
@@ -172,14 +171,14 @@ export class Signup extends Component<any, State> {
               required
               minLength={3}
               pattern="[a-zA-Z0-9_]+"
-              title={i18n.t("community_reqs")}
+              title={I18NextService.i18n.t("community_reqs")}
             />
           </div>
         </div>
 
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="register-email">
-            {i18n.t("email")}
+            {I18NextService.i18n.t("email")}
           </label>
           <div className="col-sm-10">
             <input
@@ -188,8 +187,8 @@ export class Signup extends Component<any, State> {
               className="form-control"
               placeholder={
                 siteView.local_site.require_email_verification
-                  ? i18n.t("required")
-                  : i18n.t("optional")
+                  ? I18NextService.i18n.t("required")
+                  : I18NextService.i18n.t("optional")
               }
               value={this.state.form.email}
               autoComplete="email"
@@ -202,7 +201,7 @@ export class Signup extends Component<any, State> {
               !validEmail(this.state.form.email) && (
                 <div className="mt-2 mb-0 alert alert-warning" role="alert">
                   <Icon icon="alert-triangle" classes="icon-inline me-2" />
-                  {i18n.t("no_password_reset")}
+                  {I18NextService.i18n.t("no_password_reset")}
                 </div>
               )}
           </div>
@@ -213,7 +212,7 @@ export class Signup extends Component<any, State> {
             className="col-sm-2 col-form-label"
             htmlFor="register-password"
           >
-            {i18n.t("password")}
+            {I18NextService.i18n.t("password")}
           </label>
           <div className="col-sm-10">
             <input
@@ -229,7 +228,9 @@ export class Signup extends Component<any, State> {
             />
             {this.state.form.password && (
               <div className={this.passwordColorClass}>
-                {i18n.t(this.passwordStrength as NoOptionI18nKeys)}
+                {I18NextService.i18n.t(
+                  this.passwordStrength as NoOptionI18nKeys
+                )}
               </div>
             )}
           </div>
@@ -240,7 +241,7 @@ export class Signup extends Component<any, State> {
             className="col-sm-2 col-form-label"
             htmlFor="register-verify-password"
           >
-            {i18n.t("verify_password")}
+            {I18NextService.i18n.t("verify_password")}
           </label>
           <div className="col-sm-10">
             <input
@@ -262,7 +263,7 @@ export class Signup extends Component<any, State> {
               <div className="offset-sm-2 col-sm-10">
                 <div className="mt-2 alert alert-warning" role="alert">
                   <Icon icon="alert-triangle" classes="icon-inline me-2" />
-                  {i18n.t("fill_out_application")}
+                  {I18NextService.i18n.t("fill_out_application")}
                 </div>
                 {siteView.local_site.application_question && (
                   <div
@@ -280,7 +281,7 @@ export class Signup extends Component<any, State> {
                 className="col-sm-2 col-form-label"
                 htmlFor="application_answer"
               >
-                {i18n.t("answer")}
+                {I18NextService.i18n.t("answer")}
               </label>
               <div className="col-sm-10">
                 <MarkdownTextArea
@@ -306,7 +307,7 @@ export class Signup extends Component<any, State> {
                 onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
               />
               <label className="form-check-label" htmlFor="register-show-nsfw">
-                {i18n.t("show_nsfw")}
+                {I18NextService.i18n.t("show_nsfw")}
               </label>
             </div>
           </div>
@@ -345,12 +346,14 @@ export class Signup extends Component<any, State> {
         return (
           <div className="mb-3 row">
             <label className="col-sm-2" htmlFor="register-captcha">
-              <span className="me-2">{i18n.t("enter_code")}</span>
+              <span className="me-2">
+                {I18NextService.i18n.t("enter_code")}
+              </span>
               <button
                 type="button"
                 className="btn btn-secondary"
                 onClick={linkEvent(this, this.handleRegenCaptcha)}
-                aria-label={i18n.t("captcha")}
+                aria-label={I18NextService.i18n.t("captcha")}
               >
                 <Icon icon="refresh-cw" classes="icon-refresh-cw" />
               </button>
@@ -384,13 +387,13 @@ export class Signup extends Component<any, State> {
             className="rounded-top img-fluid"
             src={this.captchaPngSrc(captchaRes)}
             style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
-            alt={i18n.t("captcha")}
+            alt={I18NextService.i18n.t("captcha")}
           />
           {captchaRes.wav && (
             <button
               className="rounded-bottom btn btn-sm btn-secondary d-block"
               style="border-top-right-radius: 0; border-top-left-radius: 0;"
-              title={i18n.t("play_captcha_audio")}
+              title={I18NextService.i18n.t("play_captcha_audio")}
               onClick={linkEvent(this, this.handleCaptchaPlay)}
               type="button"
               disabled={this.state.captchaPlaying}
@@ -474,10 +477,10 @@ export class Signup extends Component<any, State> {
             i.props.history.replace("/communities");
           } else {
             if (data.verify_email_sent) {
-              toast(i18n.t("verify_email_sent"));
+              toast(I18NextService.i18n.t("verify_email_sent"));
             }
             if (data.registration_created) {
-              toast(i18n.t("registration_application_sent"));
+              toast(I18NextService.i18n.t("registration_application_sent"));
             }
             i.props.history.push("/");
           }
index eb30cbe192dcc991ed4c5f8332d6f50c078a06b0..33e7989347ca128d68f1b0a3bd23b116a73bf981 100644 (file)
@@ -13,7 +13,7 @@ import {
   Instance,
   ListingType,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { ImageUploadForm } from "../common/image-upload-form";
 import { LanguageSelect } from "../common/language-select";
@@ -136,12 +136,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         />
         <h5>{`${
           siteSetup
-            ? capitalizeFirstLetter(i18n.t("edit"))
-            : capitalizeFirstLetter(i18n.t("setup"))
-        } ${i18n.t("your_site")}`}</h5>
+            ? capitalizeFirstLetter(I18NextService.i18n.t("edit"))
+            : capitalizeFirstLetter(I18NextService.i18n.t("setup"))
+        } ${I18NextService.i18n.t("your_site")}`}</h5>
         <div className="mb-3 row">
           <label className="col-12 col-form-label" htmlFor="create-site-name">
-            {i18n.t("name")}
+            {I18NextService.i18n.t("name")}
           </label>
           <div className="col-12">
             <input
@@ -157,9 +157,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           </div>
         </div>
         <div className="input-group mb-3">
-          <label className="me-2 col-form-label">{i18n.t("icon")}</label>
+          <label className="me-2 col-form-label">
+            {I18NextService.i18n.t("icon")}
+          </label>
           <ImageUploadForm
-            uploadTitle={i18n.t("upload_icon")}
+            uploadTitle={I18NextService.i18n.t("upload_icon")}
             imageSrc={this.state.siteForm.icon}
             onUpload={this.handleIconUpload}
             onRemove={this.handleIconRemove}
@@ -167,9 +169,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           />
         </div>
         <div className="input-group mb-3">
-          <label className="me-2 col-form-label">{i18n.t("banner")}</label>
+          <label className="me-2 col-form-label">
+            {I18NextService.i18n.t("banner")}
+          </label>
           <ImageUploadForm
-            uploadTitle={i18n.t("upload_banner")}
+            uploadTitle={I18NextService.i18n.t("upload_banner")}
             imageSrc={this.state.siteForm.banner}
             onUpload={this.handleBannerUpload}
             onRemove={this.handleBannerRemove}
@@ -177,7 +181,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         </div>
         <div className="mb-3 row">
           <label className="col-12 col-form-label" htmlFor="site-desc">
-            {i18n.t("description")}
+            {I18NextService.i18n.t("description")}
           </label>
           <div className="col-12">
             <input
@@ -191,7 +195,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           </div>
         </div>
         <div className="mb-3 row">
-          <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
+          <label className="col-12 col-form-label">
+            {I18NextService.i18n.t("sidebar")}
+          </label>
           <div className="col-12">
             <MarkdownTextArea
               initialContent={this.state.siteForm.sidebar}
@@ -204,7 +210,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         </div>
         <div className="mb-3 row">
           <label className="col-12 col-form-label">
-            {i18n.t("legal_information")}
+            {I18NextService.i18n.t("legal_information")}
           </label>
           <div className="col-12">
             <MarkdownTextArea
@@ -230,7 +236,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-downvotes"
               >
-                {i18n.t("enable_downvotes")}
+                {I18NextService.i18n.t("enable_downvotes")}
               </label>
             </div>
           </div>
@@ -249,7 +255,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-enable-nsfw"
               >
-                {i18n.t("enable_nsfw")}
+                {I18NextService.i18n.t("enable_nsfw")}
               </label>
             </div>
           </div>
@@ -260,7 +266,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               className="form-check-label me-2"
               htmlFor="create-site-registration-mode"
             >
-              {i18n.t("registration_mode")}
+              {I18NextService.i18n.t("registration_mode")}
             </label>
             <select
               id="create-site-registration-mode"
@@ -269,17 +275,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               className="form-select d-inline-block w-auto"
             >
               <option value={"RequireApplication"}>
-                {i18n.t("require_registration_application")}
+                {I18NextService.i18n.t("require_registration_application")}
+              </option>
+              <option value={"Open"}>
+                {I18NextService.i18n.t("open_registration")}
+              </option>
+              <option value={"Closed"}>
+                {I18NextService.i18n.t("close_registration")}
               </option>
-              <option value={"Open"}>{i18n.t("open_registration")}</option>
-              <option value={"Closed"}>{i18n.t("close_registration")}</option>
             </select>
           </div>
         </div>
         {this.state.siteForm.registration_mode == "RequireApplication" && (
           <div className="mb-3 row">
             <label className="col-12 col-form-label">
-              {i18n.t("application_questionnaire")}
+              {I18NextService.i18n.t("application_questionnaire")}
             </label>
             <div className="col-12">
               <MarkdownTextArea
@@ -309,7 +319,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-community-creation-admin-only"
               >
-                {i18n.t("community_creation_admin_only")}
+                {I18NextService.i18n.t("community_creation_admin_only")}
               </label>
             </div>
           </div>
@@ -331,7 +341,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-require-email-verification"
               >
-                {i18n.t("require_email_verification")}
+                {I18NextService.i18n.t("require_email_verification")}
               </label>
             </div>
           </div>
@@ -353,7 +363,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-email-admins"
               >
-                {i18n.t("application_email_admins")}
+                {I18NextService.i18n.t("application_email_admins")}
               </label>
             </div>
           </div>
@@ -372,7 +382,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-reports-email-admins"
               >
-                {i18n.t("reports_email_admins")}
+                {I18NextService.i18n.t("reports_email_admins")}
               </label>
             </div>
           </div>
@@ -383,7 +393,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               className="form-check-label me-2"
               htmlFor="create-site-default-theme"
             >
-              {i18n.t("theme")}
+              {I18NextService.i18n.t("theme")}
             </label>
             <select
               id="create-site-default-theme"
@@ -391,7 +401,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               onChange={linkEvent(this, this.handleSiteDefaultTheme)}
               className="form-select d-inline-block w-auto"
             >
-              <option value="browser">{i18n.t("browser_default")}</option>
+              <option value="browser">
+                {I18NextService.i18n.t("browser_default")}
+              </option>
               {this.props.themeList?.map(theme => (
                 <option key={theme} value={theme}>
                   {theme}
@@ -403,7 +415,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         {this.props.showLocal && (
           <form className="mb-3 row">
             <label className="col-sm-3 col-form-label">
-              {i18n.t("listing_type")}
+              {I18NextService.i18n.t("listing_type")}
             </label>
             <div className="col-sm-9">
               <ListingTypeSelect
@@ -429,7 +441,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-private-instance"
               >
-                {i18n.t("private_instance")}
+                {I18NextService.i18n.t("private_instance")}
               </label>
             </div>
           </div>
@@ -448,7 +460,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-hide-modlog-mod-names"
               >
-                {i18n.t("hide_modlog_mod_names")}
+                {I18NextService.i18n.t("hide_modlog_mod_names")}
               </label>
             </div>
           </div>
@@ -458,7 +470,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             className="col-12 col-form-label"
             htmlFor="create-site-slur-filter-regex"
           >
-            {i18n.t("slur_filter_regex")}
+            {I18NextService.i18n.t("slur_filter_regex")}
           </label>
           <div className="col-12">
             <input
@@ -485,7 +497,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             className="col-12 col-form-label"
             htmlFor="create-site-actor-name"
           >
-            {i18n.t("actor_name_max_length")}
+            {I18NextService.i18n.t("actor_name_max_length")}
           </label>
           <div className="col-12">
             <input
@@ -512,7 +524,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-federation-enabled"
               >
-                {i18n.t("federation_enabled")}
+                {I18NextService.i18n.t("federation_enabled")}
               </label>
             </div>
           </div>
@@ -537,7 +549,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                     className="form-check-label"
                     htmlFor="create-site-federation-debug"
                   >
-                    {i18n.t("federation_debug")}
+                    {I18NextService.i18n.t("federation_debug")}
                   </label>
                 </div>
               </div>
@@ -547,7 +559,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="col-12 col-form-label"
                 htmlFor="create-site-federation-worker-count"
               >
-                {i18n.t("federation_worker_count")}
+                {I18NextService.i18n.t("federation_worker_count")}
               </label>
               <div className="col-12">
                 <input
@@ -579,7 +591,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-captcha-enabled"
               >
-                {i18n.t("captcha_enabled")}
+                {I18NextService.i18n.t("captcha_enabled")}
               </label>
             </div>
           </div>
@@ -591,7 +603,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label me-2"
                 htmlFor="create-site-captcha-difficulty"
               >
-                {i18n.t("captcha_difficulty")}
+                {I18NextService.i18n.t("captcha_difficulty")}
               </label>
               <select
                 id="create-site-captcha-difficulty"
@@ -599,9 +611,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
                 className="form-select d-inline-block w-auto"
               >
-                <option value="easy">{i18n.t("easy")}</option>
-                <option value="medium">{i18n.t("medium")}</option>
-                <option value="hard">{i18n.t("hard")}</option>
+                <option value="easy">{I18NextService.i18n.t("easy")}</option>
+                <option value="medium">
+                  {I18NextService.i18n.t("medium")}
+                </option>
+                <option value="hard">{I18NextService.i18n.t("hard")}</option>
               </select>
             </div>
           </div>
@@ -616,9 +630,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               {this.props.loading ? (
                 <Spinner />
               ) : siteSetup ? (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               ) : (
-                capitalizeFirstLetter(i18n.t("create"))
+                capitalizeFirstLetter(I18NextService.i18n.t("create"))
               )}
             </button>
           </div>
@@ -634,7 +648,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     return (
       <div className="col-12 col-md-6">
         <label className="col-form-label" htmlFor={id}>
-          {i18n.t(key)}
+          {I18NextService.i18n.t(key)}
         </label>
         <div className="d-flex justify-content-between align-items-center">
           <input
index 639e1022b96f46d1863eb4b232393a98d5b03bb7..78090dd9e40661c094ecf8074e81cd178011558d 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, linkEvent } from "inferno";
 import { PersonView, Site, SiteAggregates } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
+import { I18NextService } from "../../services";
 import { Badges } from "../common/badges";
 import { BannerIconHeader } from "../common/banner-icon-header";
 import { Icon } from "../common/icon";
@@ -62,10 +62,14 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
             className="btn btn-sm"
             onClick={linkEvent(this, this.handleCollapseSidebar)}
             aria-label={
-              this.state.collapsed ? i18n.t("expand") : i18n.t("collapse")
+              this.state.collapsed
+                ? I18NextService.i18n.t("expand")
+                : I18NextService.i18n.t("collapse")
             }
             data-tippy-content={
-              this.state.collapsed ? i18n.t("expand") : i18n.t("collapse")
+              this.state.collapsed
+                ? I18NextService.i18n.t("expand")
+                : I18NextService.i18n.t("collapse")
             }
             data-bs-toggle="collapse"
             data-bs-target="#sidebarInfoBody"
@@ -104,7 +108,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
   admins(admins: PersonView[]) {
     return (
       <ul className="mt-1 list-inline small mb-0">
-        <li className="list-inline-item">{i18n.t("admins")}:</li>
+        <li className="list-inline-item">{I18NextService.i18n.t("admins")}:</li>
         {admins.map(av => (
           <li key={av.person.id} className="list-inline-item">
             <PersonListing person={av.person} />
index 60986c55dba43e0c80022dccc71a6678ce7f84c6..c79d9554b0c415292534ff2893c758e8c6101e7f 100644 (file)
@@ -2,7 +2,7 @@ import { myAuthRequired } from "@utils/app";
 import { capitalizeFirstLetter } from "@utils/helpers";
 import { Component, InfernoMouseEvent, linkEvent } from "inferno";
 import { EditSite, Tagline } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { HtmlTags } from "../common/html-tags";
 import { Icon, Spinner } from "../common/icon";
 import { MarkdownTextArea } from "../common/markdown-textarea";
@@ -27,7 +27,7 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
     super(props, context);
   }
   get documentTitle(): string {
-    return i18n.t("taglines");
+    return I18NextService.i18n.t("taglines");
   }
 
   render() {
@@ -37,7 +37,7 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
           title={this.documentTitle}
           path={this.context.router.route.match.url}
         />
-        <h5 className="col-12">{i18n.t("taglines")}</h5>
+        <h5 className="col-12">{I18NextService.i18n.t("taglines")}</h5>
         <div className="table-responsive col-12">
           <table id="taglines_table" className="table table-sm table-hover">
             <thead className="pointer">
@@ -68,8 +68,8 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
                         { i: this, index: index },
                         this.handleEditTaglineClick
                       )}
-                      data-tippy-content={i18n.t("edit")}
-                      aria-label={i18n.t("edit")}
+                      data-tippy-content={I18NextService.i18n.t("edit")}
+                      aria-label={I18NextService.i18n.t("edit")}
                     >
                       <Icon icon="edit" classes={`icon-inline`} />
                     </button>
@@ -80,8 +80,8 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
                         { i: this, index: index },
                         this.handleDeleteTaglineClick
                       )}
-                      data-tippy-content={i18n.t("delete")}
-                      aria-label={i18n.t("delete")}
+                      data-tippy-content={I18NextService.i18n.t("delete")}
+                      aria-label={I18NextService.i18n.t("delete")}
                     >
                       <Icon icon="trash" classes={`icon-inline text-danger`} />
                     </button>
@@ -96,7 +96,7 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
                 className="btn btn-sm btn-secondary me-2"
                 onClick={linkEvent(this, this.handleAddTaglineClick)}
               >
-                {i18n.t("add_tagline")}
+                {I18NextService.i18n.t("add_tagline")}
               </button>
             </div>
           </div>
@@ -111,7 +111,7 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
                 {this.props.loading ? (
                   <Spinner />
                 ) : (
-                  capitalizeFirstLetter(i18n.t("save"))
+                  capitalizeFirstLetter(I18NextService.i18n.t("save"))
                 )}
               </button>
             </div>
index edced0f4b484b2be1e9fe72b5889a5bced8ebeca..e705bac86694affe85e7f72d7dbe71ece7b0e841 100644 (file)
@@ -46,9 +46,8 @@ import {
 } from "lemmy-js-client";
 import moment from "moment";
 import { fetchLimit } from "../config";
-import { i18n } from "../i18next";
 import { InitialFetchRequest } from "../interfaces";
-import { FirstLoadService } from "../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../services";
 import { HttpService, RequestState } from "../services/HttpService";
 import { HtmlTags } from "./common/html-tags";
 import { Icon, Spinner } from "./common/icon";
@@ -586,14 +585,14 @@ const Filter = ({
 }) => (
   <div className="col-sm-6 mb-3">
     <label className="mb-2" htmlFor={`filter-${filterType}`}>
-      {i18n.t(`filter_by_${filterType}` as NoOptionI18nKeys)}
+      {I18NextService.i18n.t(`filter_by_${filterType}` as NoOptionI18nKeys)}
     </label>
     <SearchableSelect
       id={`filter-${filterType}`}
       value={value ?? 0}
       options={[
         {
-          label: i18n.t("all"),
+          label: I18NextService.i18n.t("all"),
           value: "0",
         },
       ].concat(options)}
@@ -724,8 +723,8 @@ export class Modlog extends Component<
       this.isoData.site_res.admins.some(
         ({ person: { id } }) => id === person.id
       )
-      ? i18n.t("admin")
-      : i18n.t("mod");
+      ? I18NextService.i18n.t("admin")
+      : I18NextService.i18n.t("mod");
   }
 
   get documentTitle(): string {
@@ -770,7 +769,7 @@ export class Modlog extends Component<
               >
                 /c/{this.state.communityRes.data.community_view.community.name}{" "}
               </Link>
-              <span>{i18n.t("modlog")}</span>
+              <span>{I18NextService.i18n.t("modlog")}</span>
             </h5>
           )}
           <div className="row mb-2">
@@ -782,9 +781,9 @@ export class Modlog extends Component<
                 aria-label="action"
               >
                 <option disabled aria-hidden="true">
-                  {i18n.t("filter_by_action")}
+                  {I18NextService.i18n.t("filter_by_action")}
                 </option>
-                <option value={"All"}>{i18n.t("all")}</option>
+                <option value={"All"}>{I18NextService.i18n.t("all")}</option>
                 <option value={"ModRemovePost"}>Removing Posts</option>
                 <option value={"ModLockPost"}>Locking Posts</option>
                 <option value={"ModFeaturePost"}>Featuring Posts</option>
@@ -848,9 +847,9 @@ export class Modlog extends Component<
             <table id="modlog_table" className="table table-sm table-hover">
               <thead className="pointer">
                 <tr>
-                  <th> {i18n.t("time")}</th>
-                  <th>{i18n.t("mod")}</th>
-                  <th>{i18n.t("action")}</th>
+                  <th> {I18NextService.i18n.t("time")}</th>
+                  <th>{I18NextService.i18n.t("mod")}</th>
+                  <th>{I18NextService.i18n.t("action")}</th>
                 </tr>
               </thead>
               {this.combined}
index d083f26fe352d9160a8acebde800ccc7383d17e7..ddb12beaa86f458516f7914bc40edc9ebab3d7db 100644 (file)
@@ -1,5 +1,5 @@
 import { Component } from "inferno";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon } from "../common/icon";
 
 interface CakeDayProps {
@@ -19,6 +19,8 @@ export class CakeDay extends Component<CakeDayProps, any> {
   }
 
   cakeDayTippy(): string {
-    return i18n.t("cake_day_info", { creator_name: this.props.creatorName });
+    return I18NextService.i18n.t("cake_day_info", {
+      creator_name: this.props.creatorName,
+    });
   }
 }
index 91bbee03eea14a241285b8eb69338552bc63a65c..062fc01c5e7bdb70b17a16f613b9ac498f646d2d 100644 (file)
@@ -59,10 +59,8 @@ import {
   TransferCommunity,
 } from "lemmy-js-client";
 import { fetchLimit, relTags } from "../../config";
-import { i18n } from "../../i18next";
 import { CommentViewType, InitialFetchRequest } from "../../interfaces";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { CommentNodes } from "../comment/comment-nodes";
@@ -187,9 +185,9 @@ export class Inbox extends Component<any, InboxState> {
   get documentTitle(): string {
     const mui = UserService.Instance.myUserInfo;
     return mui
-      ? `@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
-          this.state.siteRes.site_view.site.name
-        }`
+      ? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
+          "inbox"
+        )} - ${this.state.siteRes.site_view.site.name}`
       : "";
   }
 
@@ -223,7 +221,7 @@ export class Inbox extends Component<any, InboxState> {
               path={this.context.router.route.match.url}
             />
             <h5 className="mb-2">
-              {i18n.t("inbox")}
+              {I18NextService.i18n.t("inbox")}
               {inboxRss && (
                 <small>
                   <a href={inboxRss} title="RSS" rel={relTags}>
@@ -245,7 +243,7 @@ export class Inbox extends Component<any, InboxState> {
                 {this.state.markAllAsReadRes.state == "loading" ? (
                   <Spinner />
                 ) : (
-                  i18n.t("mark_all_as_read")
+                  I18NextService.i18n.t("mark_all_as_read")
                 )}
               </button>
             )}
@@ -296,7 +294,7 @@ export class Inbox extends Component<any, InboxState> {
             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
           />
-          {i18n.t("unread")}
+          {I18NextService.i18n.t("unread")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -310,7 +308,7 @@ export class Inbox extends Component<any, InboxState> {
             checked={this.state.unreadOrAll == UnreadOrAll.All}
             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
           />
-          {i18n.t("all")}
+          {I18NextService.i18n.t("all")}
         </label>
       </div>
     );
@@ -331,7 +329,7 @@ export class Inbox extends Component<any, InboxState> {
             checked={this.state.messageType == MessageType.All}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("all")}
+          {I18NextService.i18n.t("all")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -345,7 +343,7 @@ export class Inbox extends Component<any, InboxState> {
             checked={this.state.messageType == MessageType.Replies}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("replies")}
+          {I18NextService.i18n.t("replies")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -359,7 +357,7 @@ export class Inbox extends Component<any, InboxState> {
             checked={this.state.messageType == MessageType.Mentions}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("mentions")}
+          {I18NextService.i18n.t("mentions")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -373,7 +371,7 @@ export class Inbox extends Component<any, InboxState> {
             checked={this.state.messageType == MessageType.Messages}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("messages")}
+          {I18NextService.i18n.t("messages")}
         </label>
       </div>
     );
@@ -826,7 +824,7 @@ export class Inbox extends Component<any, InboxState> {
     const res = await HttpService.client.createComment(form);
 
     if (res.state === "success") {
-      toast(i18n.t("reply_sent"));
+      toast(I18NextService.i18n.t("reply_sent"));
       this.findAndUpdateComment(res);
     }
 
@@ -837,7 +835,7 @@ export class Inbox extends Component<any, InboxState> {
     const res = await HttpService.client.editComment(form);
 
     if (res.state === "success") {
-      toast(i18n.t("edit"));
+      toast(I18NextService.i18n.t("edit"));
       this.findAndUpdateComment(res);
     } else if (res.state === "failed") {
       toast(res.msg, "danger");
@@ -849,7 +847,7 @@ export class Inbox extends Component<any, InboxState> {
   async handleDeleteComment(form: DeleteComment) {
     const res = await HttpService.client.deleteComment(form);
     if (res.state == "success") {
-      toast(i18n.t("deleted"));
+      toast(I18NextService.i18n.t("deleted"));
       this.findAndUpdateComment(res);
     }
   }
@@ -857,7 +855,7 @@ export class Inbox extends Component<any, InboxState> {
   async handleRemoveComment(form: RemoveComment) {
     const res = await HttpService.client.removeComment(form);
     if (res.state == "success") {
-      toast(i18n.t("remove_comment"));
+      toast(I18NextService.i18n.t("remove_comment"));
       this.findAndUpdateComment(res);
     }
   }
@@ -892,7 +890,7 @@ export class Inbox extends Component<any, InboxState> {
 
   async handleTransferCommunity(form: TransferCommunity) {
     await HttpService.client.transferCommunity(form);
-    toast(i18n.t("transfer_community"));
+    toast(I18NextService.i18n.t("transfer_community"));
   }
 
   async handleCommentReplyRead(form: MarkCommentReplyAsRead) {
@@ -1004,7 +1002,7 @@ export class Inbox extends Component<any, InboxState> {
 
   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
     if (purgeRes.state == "success") {
-      toast(i18n.t("purge_success"));
+      toast(I18NextService.i18n.t("purge_success"));
       this.context.router.history.push(`/`);
     }
   }
@@ -1013,7 +1011,7 @@ export class Inbox extends Component<any, InboxState> {
     res: RequestState<PrivateMessageReportResponse | CommentReportResponse>
   ) {
     if (res.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
index e20c31382d2fa1f469f2ab5cb419652ca39df809..dd85e05b3a1961e9f91975ac74185f8db8489291 100644 (file)
@@ -2,8 +2,7 @@ import { myAuth, setIsoData } from "@utils/app";
 import { capitalizeFirstLetter } from "@utils/helpers";
 import { Component, linkEvent } from "inferno";
 import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { HttpService, UserService } from "../../services";
+import { HttpService, I18NextService, UserService } from "../../services";
 import { RequestState } from "../../services/HttpService";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
@@ -34,7 +33,7 @@ export class PasswordChange extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("password_change")} - ${
+    return `${I18NextService.i18n.t("password_change")} - ${
       this.state.siteRes.site_view.site.name
     }`;
   }
@@ -48,7 +47,7 @@ export class PasswordChange extends Component<any, State> {
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-            <h5>{i18n.t("password_change")}</h5>
+            <h5>{I18NextService.i18n.t("password_change")}</h5>
             {this.passwordChangeForm()}
           </div>
         </div>
@@ -61,7 +60,7 @@ export class PasswordChange extends Component<any, State> {
       <form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="new-password">
-            {i18n.t("new_password")}
+            {I18NextService.i18n.t("new_password")}
           </label>
           <div className="col-sm-10">
             <input
@@ -77,7 +76,7 @@ export class PasswordChange extends Component<any, State> {
         </div>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="verify-password">
-            {i18n.t("verify_password")}
+            {I18NextService.i18n.t("verify_password")}
           </label>
           <div className="col-sm-10">
             <input
@@ -97,7 +96,7 @@ export class PasswordChange extends Component<any, State> {
               {this.state.passwordChangeRes.state == "loading" ? (
                 <Spinner />
               ) : (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               )}
             </button>
           </div>
index 763947e8e1b07f50e1fe78d158c34876588fe9b1..d00036871a04c9ec922c93a56e5c9b6eb43fec8b 100644 (file)
@@ -72,11 +72,9 @@ import {
 } from "lemmy-js-client";
 import moment from "moment";
 import { fetchLimit, relTags } from "../../config";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
 import { mdToHtml } from "../../markdown";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { setupTippy } from "../../tippy";
 import { toast } from "../../toast";
@@ -137,7 +135,7 @@ const getCommunitiesListing = (
   communityViews.length > 0 && (
     <div className="card border-secondary mb-3">
       <div className="card-body">
-        <h5>{i18n.t(translationKey)}</h5>
+        <h5>{I18NextService.i18n.t(translationKey)}</h5>
         <ul className="list-unstyled mb-0">
           {communityViews.map(({ community }) => (
             <li key={community.id}>
@@ -422,7 +420,7 @@ export class Profile extends Component<
           checked={active}
           onChange={linkEvent(this, this.handleViewChange)}
         />
-        {i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
+        {I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
       </label>
     );
   }
@@ -485,22 +483,22 @@ export class Profile extends Component<
                     </li>
                     {isBanned(pv.person) && (
                       <li className="list-inline-item badge text-bg-danger">
-                        {i18n.t("banned")}
+                        {I18NextService.i18n.t("banned")}
                       </li>
                     )}
                     {pv.person.deleted && (
                       <li className="list-inline-item badge text-bg-danger">
-                        {i18n.t("deleted")}
+                        {I18NextService.i18n.t("deleted")}
                       </li>
                     )}
                     {pv.person.admin && (
                       <li className="list-inline-item badge text-bg-light">
-                        {i18n.t("admin")}
+                        {I18NextService.i18n.t("admin")}
                       </li>
                     )}
                     {pv.person.bot_account && (
                       <li className="list-inline-item badge text-bg-light">
-                        {i18n.t("bot_account").toLowerCase()}
+                        {I18NextService.i18n.t("bot_account").toLowerCase()}
                       </li>
                     )}
                   </ul>
@@ -516,7 +514,7 @@ export class Profile extends Component<
                       rel={relTags}
                       href={`https://matrix.to/#/${pv.person.matrix_user_id}`}
                     >
-                      {i18n.t("send_secure_message")}
+                      {I18NextService.i18n.t("send_secure_message")}
                     </a>
                     <Link
                       className={
@@ -524,7 +522,7 @@ export class Profile extends Component<
                       }
                       to={`/create_private_message/${pv.person.id}`}
                     >
-                      {i18n.t("send_message")}
+                      {I18NextService.i18n.t("send_message")}
                     </Link>
                     {personBlocked ? (
                       <button
@@ -536,7 +534,7 @@ export class Profile extends Component<
                           this.handleUnblockPerson
                         )}
                       >
-                        {i18n.t("unblock_user")}
+                        {I18NextService.i18n.t("unblock_user")}
                       </button>
                     ) : (
                       <button
@@ -548,7 +546,7 @@ export class Profile extends Component<
                           this.handleBlockPerson
                         )}
                       >
-                        {i18n.t("block_user")}
+                        {I18NextService.i18n.t("block_user")}
                       </button>
                     )}
                   </>
@@ -563,9 +561,9 @@ export class Profile extends Component<
                         "d-flex align-self-start btn btn-secondary me-2"
                       }
                       onClick={linkEvent(this, this.handleModBanShow)}
-                      aria-label={i18n.t("ban")}
+                      aria-label={I18NextService.i18n.t("ban")}
                     >
-                      {capitalizeFirstLetter(i18n.t("ban"))}
+                      {capitalizeFirstLetter(I18NextService.i18n.t("ban"))}
                     </button>
                   ) : (
                     <button
@@ -573,9 +571,9 @@ export class Profile extends Component<
                         "d-flex align-self-start btn btn-secondary me-2"
                       }
                       onClick={linkEvent(this, this.handleModBanSubmit)}
-                      aria-label={i18n.t("unban")}
+                      aria-label={I18NextService.i18n.t("unban")}
                     >
-                      {capitalizeFirstLetter(i18n.t("unban"))}
+                      {capitalizeFirstLetter(I18NextService.i18n.t("unban"))}
                     </button>
                   ))}
               </div>
@@ -590,13 +588,13 @@ export class Profile extends Component<
               <div>
                 <ul className="list-inline mb-2">
                   <li className="list-inline-item badge text-bg-light">
-                    {i18n.t("number_of_posts", {
+                    {I18NextService.i18n.t("number_of_posts", {
                       count: Number(pv.counts.post_count),
                       formattedCount: numToSI(pv.counts.post_count),
                     })}
                   </li>
                   <li className="list-inline-item badge text-bg-light">
-                    {i18n.t("number_of_comments", {
+                    {I18NextService.i18n.t("number_of_comments", {
                       count: Number(pv.counts.comment_count),
                       formattedCount: numToSI(pv.counts.comment_count),
                     })}
@@ -604,7 +602,7 @@ export class Profile extends Component<
                 </ul>
               </div>
               <div className="text-muted">
-                {i18n.t("joined")}{" "}
+                {I18NextService.i18n.t("joined")}{" "}
                 <MomentTime
                   published={pv.person.published}
                   showAgo
@@ -614,7 +612,7 @@ export class Profile extends Component<
               <div className="d-flex align-items-center text-muted mb-2">
                 <Icon icon="cake" />
                 <span className="ms-2">
-                  {i18n.t("cake_day_title")}{" "}
+                  {I18NextService.i18n.t("cake_day_title")}{" "}
                   {moment
                     .utc(pv.person.published)
                     .local()
@@ -623,7 +621,7 @@ export class Profile extends Component<
               </div>
               {!UserService.Instance.myUserInfo && (
                 <div className="alert alert-info" role="alert">
-                  {i18n.t("profile_not_logged_in_alert")}
+                  {I18NextService.i18n.t("profile_not_logged_in_alert")}
                 </div>
               )}
             </div>
@@ -641,24 +639,24 @@ export class Profile extends Component<
         <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
           <div className="mb-3 row col-12">
             <label className="col-form-label" htmlFor="profile-ban-reason">
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id="profile-ban-reason"
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               value={this.state.banReason}
               onInput={linkEvent(this, this.handleModBanReasonChange)}
             />
             <label className="col-form-label" htmlFor={`mod-ban-expires`}>
-              {i18n.t("expires")}
+              {I18NextService.i18n.t("expires")}
             </label>
             <input
               type="number"
               id={`mod-ban-expires`}
               className="form-control me-2"
-              placeholder={i18n.t("number_of_days")}
+              placeholder={I18NextService.i18n.t("number_of_days")}
               value={this.state.banExpireDays}
               onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
             />
@@ -674,9 +672,9 @@ export class Profile extends Component<
                 <label
                   className="form-check-label"
                   htmlFor="mod-ban-remove-data"
-                  title={i18n.t("remove_content_more")}
+                  title={I18NextService.i18n.t("remove_content_more")}
                 >
-                  {i18n.t("remove_content")}
+                  {I18NextService.i18n.t("remove_content")}
                 </label>
               </div>
             </div>
@@ -684,23 +682,23 @@ export class Profile extends Component<
           {/* TODO hold off on expires until later */}
           {/* <div class="mb-3 row"> */}
           {/*   <label class="col-form-label">Expires</label> */}
-          {/*   <input type="date" class="form-control me-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
+          {/*   <input type="date" class="form-control me-2" placeholder={I18NextService.i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
           {/* </div> */}
           <div className="mb-3 row">
             <button
               type="reset"
               className="btn btn-secondary me-2"
-              aria-label={i18n.t("cancel")}
+              aria-label={I18NextService.i18n.t("cancel")}
               onClick={linkEvent(this, this.handleModBanSubmitCancel)}
             >
-              {i18n.t("cancel")}
+              {I18NextService.i18n.t("cancel")}
             </button>
             <button
               type="submit"
               className="btn btn-secondary"
-              aria-label={i18n.t("ban")}
+              aria-label={I18NextService.i18n.t("ban")}
             >
-              {i18n.t("ban")} {pv.person.name}
+              {I18NextService.i18n.t("ban")} {pv.person.name}
             </button>
           </div>
         </form>
@@ -904,14 +902,14 @@ export class Profile extends Component<
   async handleCommentReport(form: CreateCommentReport) {
     const reportRes = await HttpService.client.createCommentReport(form);
     if (reportRes.state === "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
   async handlePostReport(form: CreatePostReport) {
     const reportRes = await HttpService.client.createPostReport(form);
     if (reportRes.state === "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
@@ -935,7 +933,7 @@ export class Profile extends Component<
 
   async handleTransferCommunity(form: TransferCommunity) {
     await HttpService.client.transferCommunity(form);
-    toast(i18n.t("transfer_community"));
+    toast(I18NextService.i18n.t("transfer_community"));
   }
 
   async handleCommentReplyRead(form: MarkCommentReplyAsRead) {
@@ -999,7 +997,7 @@ export class Profile extends Component<
 
   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
     if (purgeRes.state == "success") {
-      toast(i18n.t("purge_success"));
+      toast(I18NextService.i18n.t("purge_success"));
       this.context.router.history.push(`/`);
     }
   }
index 0e636fc77352a95afc6765a9ba4df99c3dd610b6..d9eb6ad0a65ed1d9f858f90a688c377f47e51685 100644 (file)
@@ -12,10 +12,8 @@ import {
   RegistrationApplicationView,
 } from "lemmy-js-client";
 import { fetchLimit } from "../../config";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { setupTippy } from "../../tippy";
 import { HtmlTags } from "../common/html-tags";
@@ -79,7 +77,7 @@ export class RegistrationApplications extends Component<
   get documentTitle(): string {
     const mui = UserService.Instance.myUserInfo;
     return mui
-      ? `@${mui.local_user_view.person.name} ${i18n.t(
+      ? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
           "registration_applications"
         )} - ${this.state.siteRes.site_view.site.name}`
       : "";
@@ -102,7 +100,9 @@ export class RegistrationApplications extends Component<
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
               />
-              <h5 className="mb-2">{i18n.t("registration_applications")}</h5>
+              <h5 className="mb-2">
+                {I18NextService.i18n.t("registration_applications")}
+              </h5>
               {this.selects()}
               {this.applicationList(apps)}
               <Paginator
@@ -139,7 +139,7 @@ export class RegistrationApplications extends Component<
             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
           />
-          {i18n.t("unread")}
+          {I18NextService.i18n.t("unread")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -153,7 +153,7 @@ export class RegistrationApplications extends Component<
             checked={this.state.unreadOrAll == UnreadOrAll.All}
             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
           />
-          {i18n.t("all")}
+          {I18NextService.i18n.t("all")}
         </label>
       </div>
     );
index 6fe59f1c4bafb2a74835e4168ad8a0d7e0bb6f0d..8f5eaf366b6dcf0d99022ae2d8a13d455b85762e 100644 (file)
@@ -27,10 +27,13 @@ import {
   ResolvePrivateMessageReport,
 } from "lemmy-js-client";
 import { fetchLimit } from "../../config";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
-import { HttpService, UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import {
+  FirstLoadService,
+  HttpService,
+  I18NextService,
+  UserService,
+} from "../../services";
 import { RequestState } from "../../services/HttpService";
 import { CommentReport } from "../comment/comment-report";
 import { HtmlTags } from "../common/html-tags";
@@ -134,9 +137,9 @@ export class Reports extends Component<any, ReportsState> {
   get documentTitle(): string {
     const mui = UserService.Instance.myUserInfo;
     return mui
-      ? `@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
-          this.state.siteRes.site_view.site.name
-        }`
+      ? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
+          "reports"
+        )} - ${this.state.siteRes.site_view.site.name}`
       : "";
   }
 
@@ -149,7 +152,7 @@ export class Reports extends Component<any, ReportsState> {
               title={this.documentTitle}
               path={this.context.router.route.match.url}
             />
-            <h5 className="mb-2">{i18n.t("reports")}</h5>
+            <h5 className="mb-2">{I18NextService.i18n.t("reports")}</h5>
             {this.selects()}
             {this.section}
             <Paginator
@@ -198,7 +201,7 @@ export class Reports extends Component<any, ReportsState> {
             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
           />
-          {i18n.t("unread")}
+          {I18NextService.i18n.t("unread")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -212,7 +215,7 @@ export class Reports extends Component<any, ReportsState> {
             checked={this.state.unreadOrAll == UnreadOrAll.All}
             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
           />
-          {i18n.t("all")}
+          {I18NextService.i18n.t("all")}
         </label>
       </div>
     );
@@ -233,7 +236,7 @@ export class Reports extends Component<any, ReportsState> {
             checked={this.state.messageType == MessageType.All}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("all")}
+          {I18NextService.i18n.t("all")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -247,7 +250,7 @@ export class Reports extends Component<any, ReportsState> {
             checked={this.state.messageType == MessageType.CommentReport}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("comments")}
+          {I18NextService.i18n.t("comments")}
         </label>
         <label
           className={`btn btn-outline-secondary pointer
@@ -261,7 +264,7 @@ export class Reports extends Component<any, ReportsState> {
             checked={this.state.messageType == MessageType.PostReport}
             onChange={linkEvent(this, this.handleMessageTypeChange)}
           />
-          {i18n.t("posts")}
+          {I18NextService.i18n.t("posts")}
         </label>
         {amAdmin() && (
           <label
@@ -281,7 +284,7 @@ export class Reports extends Component<any, ReportsState> {
               }
               onChange={linkEvent(this, this.handleMessageTypeChange)}
             />
-            {i18n.t("messages")}
+            {I18NextService.i18n.t("messages")}
           </label>
         )}
       </div>
index 5f149dfe2960b569f17262c9874cd3a9819175db..dc542e72594518b49c5efd3f39c8be1431ea1722 100644 (file)
@@ -29,9 +29,9 @@ import {
   SortType,
 } from "lemmy-js-client";
 import { elementUrl, emDash, relTags } from "../../config";
-import { i18n, languages } from "../../i18next";
 import { UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
+import { I18NextService, languages } from "../../services/I18NextService";
 import { setupTippy } from "../../tippy";
 import { toast } from "../../toast";
 import { HtmlTags } from "../common/html-tags";
@@ -113,7 +113,7 @@ const Filter = ({
       className="col-md-4 col-form-label"
       htmlFor={`block-${filterType}-filter`}
     >
-      {i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
+      {I18NextService.i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
     </label>
     <div className="col-md-8">
       <SearchableSelect
@@ -233,7 +233,7 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   get documentTitle(): string {
-    return i18n.t("settings");
+    return I18NextService.i18n.t("settings");
   }
 
   render() {
@@ -249,12 +249,12 @@ export class Settings extends Component<any, SettingsState> {
           tabs={[
             {
               key: "settings",
-              label: i18n.t("settings"),
+              label: I18NextService.i18n.t("settings"),
               getNode: this.userSettings,
             },
             {
               key: "blocks",
-              label: i18n.t("blocks"),
+              label: I18NextService.i18n.t("blocks"),
               getNode: this.blockCards,
             },
           ]}
@@ -316,11 +316,11 @@ export class Settings extends Component<any, SettingsState> {
   changePasswordHtmlForm() {
     return (
       <>
-        <h5>{i18n.t("change_password")}</h5>
+        <h5>{I18NextService.i18n.t("change_password")}</h5>
         <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
           <div className="mb-3 row">
             <label className="col-sm-5 col-form-label" htmlFor="user-password">
-              {i18n.t("new_password")}
+              {I18NextService.i18n.t("new_password")}
             </label>
             <div className="col-sm-7">
               <input
@@ -339,7 +339,7 @@ export class Settings extends Component<any, SettingsState> {
               className="col-sm-5 col-form-label"
               htmlFor="user-verify-password"
             >
-              {i18n.t("verify_password")}
+              {I18NextService.i18n.t("verify_password")}
             </label>
             <div className="col-sm-7">
               <input
@@ -358,7 +358,7 @@ export class Settings extends Component<any, SettingsState> {
               className="col-sm-5 col-form-label"
               htmlFor="user-old-password"
             >
-              {i18n.t("old_password")}
+              {I18NextService.i18n.t("old_password")}
             </label>
             <div className="col-sm-7">
               <input
@@ -380,7 +380,7 @@ export class Settings extends Component<any, SettingsState> {
               {this.state.changePasswordRes.state === "loading" ? (
                 <Spinner />
               ) : (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               )}
             </button>
           </div>
@@ -409,7 +409,7 @@ export class Settings extends Component<any, SettingsState> {
   blockedUsersList() {
     return (
       <>
-        <h5>{i18n.t("blocked_users")}</h5>
+        <h5>{I18NextService.i18n.t("blocked_users")}</h5>
         <ul className="list-unstyled mb-0">
           {this.state.personBlocks.map(pb => (
             <li key={pb.target.id}>
@@ -421,7 +421,7 @@ export class Settings extends Component<any, SettingsState> {
                     { ctx: this, recipientId: pb.target.id },
                     this.handleUnblockPerson
                   )}
-                  data-tippy-content={i18n.t("unblock_user")}
+                  data-tippy-content={I18NextService.i18n.t("unblock_user")}
                 >
                   <Icon icon="x" classes="icon-inline" />
                 </button>
@@ -453,7 +453,7 @@ export class Settings extends Component<any, SettingsState> {
   blockedCommunitiesList() {
     return (
       <>
-        <h5>{i18n.t("blocked_communities")}</h5>
+        <h5>{I18NextService.i18n.t("blocked_communities")}</h5>
         <ul className="list-unstyled mb-0">
           {this.state.communityBlocks.map(cb => (
             <li key={cb.community.id}>
@@ -465,7 +465,9 @@ export class Settings extends Component<any, SettingsState> {
                     { ctx: this, communityId: cb.community.id },
                     this.handleUnblockCommunity
                   )}
-                  data-tippy-content={i18n.t("unblock_community")}
+                  data-tippy-content={I18NextService.i18n.t(
+                    "unblock_community"
+                  )}
                 >
                   <Icon icon="x" classes="icon-inline" />
                 </button>
@@ -482,18 +484,18 @@ export class Settings extends Component<any, SettingsState> {
 
     return (
       <>
-        <h5>{i18n.t("settings")}</h5>
+        <h5>{I18NextService.i18n.t("settings")}</h5>
         <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label" htmlFor="display-name">
-              {i18n.t("display_name")}
+              {I18NextService.i18n.t("display_name")}
             </label>
             <div className="col-sm-9">
               <input
                 id="display-name"
                 type="text"
                 className="form-control"
-                placeholder={i18n.t("optional")}
+                placeholder={I18NextService.i18n.t("optional")}
                 value={this.state.saveUserSettingsForm.display_name}
                 onInput={linkEvent(this, this.handleDisplayNameChange)}
                 pattern="^(?!@)(.+)$"
@@ -503,7 +505,7 @@ export class Settings extends Component<any, SettingsState> {
           </div>
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label" htmlFor="user-bio">
-              {i18n.t("bio")}
+              {I18NextService.i18n.t("bio")}
             </label>
             <div className="col-sm-9">
               <MarkdownTextArea
@@ -518,14 +520,14 @@ export class Settings extends Component<any, SettingsState> {
           </div>
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label" htmlFor="user-email">
-              {i18n.t("email")}
+              {I18NextService.i18n.t("email")}
             </label>
             <div className="col-sm-9">
               <input
                 type="email"
                 id="user-email"
                 className="form-control"
-                placeholder={i18n.t("optional")}
+                placeholder={I18NextService.i18n.t("optional")}
                 value={this.state.saveUserSettingsForm.email}
                 onInput={linkEvent(this, this.handleEmailChange)}
                 minLength={3}
@@ -535,7 +537,7 @@ export class Settings extends Component<any, SettingsState> {
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label" htmlFor="matrix-user-id">
               <a href={elementUrl} rel={relTags}>
-                {i18n.t("matrix_user_id")}
+                {I18NextService.i18n.t("matrix_user_id")}
               </a>
             </label>
             <div className="col-sm-9">
@@ -552,11 +554,11 @@ export class Settings extends Component<any, SettingsState> {
           </div>
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label">
-              {i18n.t("avatar")}
+              {I18NextService.i18n.t("avatar")}
             </label>
             <div className="col-sm-9">
               <ImageUploadForm
-                uploadTitle={i18n.t("upload_avatar")}
+                uploadTitle={I18NextService.i18n.t("upload_avatar")}
                 imageSrc={this.state.saveUserSettingsForm.avatar}
                 onUpload={this.handleAvatarUpload}
                 onRemove={this.handleAvatarRemove}
@@ -566,11 +568,11 @@ export class Settings extends Component<any, SettingsState> {
           </div>
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label">
-              {i18n.t("banner")}
+              {I18NextService.i18n.t("banner")}
             </label>
             <div className="col-sm-9">
               <ImageUploadForm
-                uploadTitle={i18n.t("upload_banner")}
+                uploadTitle={I18NextService.i18n.t("upload_banner")}
                 imageSrc={this.state.saveUserSettingsForm.banner}
                 onUpload={this.handleBannerUpload}
                 onRemove={this.handleBannerRemove}
@@ -579,7 +581,7 @@ export class Settings extends Component<any, SettingsState> {
           </div>
           <div className="mb-3 row">
             <label className="col-sm-3 form-label" htmlFor="user-language">
-              {i18n.t("interface_language")}
+              {I18NextService.i18n.t("interface_language")}
             </label>
             <div className="col-sm-9">
               <select
@@ -589,9 +591,11 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-select d-inline-block w-auto"
               >
                 <option disabled aria-hidden="true">
-                  {i18n.t("interface_language")}
+                  {I18NextService.i18n.t("interface_language")}
+                </option>
+                <option value="browser">
+                  {I18NextService.i18n.t("browser_default")}
                 </option>
-                <option value="browser">{i18n.t("browser_default")}</option>
                 <option disabled aria-hidden="true">
                   ──
                 </option>
@@ -616,7 +620,7 @@ export class Settings extends Component<any, SettingsState> {
           />
           <div className="mb-3 row">
             <label className="col-sm-3 col-form-label" htmlFor="user-theme">
-              {i18n.t("theme")}
+              {I18NextService.i18n.t("theme")}
             </label>
             <div className="col-sm-9">
               <select
@@ -626,9 +630,11 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-select d-inline-block w-auto"
               >
                 <option disabled aria-hidden="true">
-                  {i18n.t("theme")}
+                  {I18NextService.i18n.t("theme")}
+                </option>
+                <option value="browser">
+                  {I18NextService.i18n.t("browser_default")}
                 </option>
-                <option value="browser">{i18n.t("browser_default")}</option>
                 {this.state.themeList.map(theme => (
                   <option key={theme} value={theme}>
                     {theme}
@@ -638,7 +644,9 @@ export class Settings extends Component<any, SettingsState> {
             </div>
           </div>
           <form className="mb-3 row">
-            <label className="col-sm-3 col-form-label">{i18n.t("type")}</label>
+            <label className="col-sm-3 col-form-label">
+              {I18NextService.i18n.t("type")}
+            </label>
             <div className="col-sm-9">
               <ListingTypeSelect
                 type_={
@@ -653,7 +661,7 @@ export class Settings extends Component<any, SettingsState> {
           </form>
           <form className="mb-3 row">
             <label className="col-sm-3 col-form-label">
-              {i18n.t("sort_type")}
+              {I18NextService.i18n.t("sort_type")}
             </label>
             <div className="col-sm-9">
               <SortSelect
@@ -674,7 +682,7 @@ export class Settings extends Component<any, SettingsState> {
                 onChange={linkEvent(this, this.handleShowNsfwChange)}
               />
               <label className="form-check-label" htmlFor="user-show-nsfw">
-                {i18n.t("show_nsfw")}
+                {I18NextService.i18n.t("show_nsfw")}
               </label>
             </div>
           </div>
@@ -688,7 +696,7 @@ export class Settings extends Component<any, SettingsState> {
                 onChange={linkEvent(this, this.handleShowScoresChange)}
               />
               <label className="form-check-label" htmlFor="user-show-scores">
-                {i18n.t("show_scores")}
+                {I18NextService.i18n.t("show_scores")}
               </label>
             </div>
           </div>
@@ -702,7 +710,7 @@ export class Settings extends Component<any, SettingsState> {
                 onChange={linkEvent(this, this.handleShowAvatarsChange)}
               />
               <label className="form-check-label" htmlFor="user-show-avatars">
-                {i18n.t("show_avatars")}
+                {I18NextService.i18n.t("show_avatars")}
               </label>
             </div>
           </div>
@@ -716,7 +724,7 @@ export class Settings extends Component<any, SettingsState> {
                 onChange={linkEvent(this, this.handleBotAccount)}
               />
               <label className="form-check-label" htmlFor="user-bot-account">
-                {i18n.t("bot_account")}
+                {I18NextService.i18n.t("bot_account")}
               </label>
             </div>
           </div>
@@ -733,7 +741,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-label"
                 htmlFor="user-show-bot-accounts"
               >
-                {i18n.t("show_bot_accounts")}
+                {I18NextService.i18n.t("show_bot_accounts")}
               </label>
             </div>
           </div>
@@ -750,7 +758,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-label"
                 htmlFor="user-show-read-posts"
               >
-                {i18n.t("show_read_posts")}
+                {I18NextService.i18n.t("show_read_posts")}
               </label>
             </div>
           </div>
@@ -767,7 +775,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-label"
                 htmlFor="user-show-new-post-notifs"
               >
-                {i18n.t("show_new_post_notifs")}
+                {I18NextService.i18n.t("show_new_post_notifs")}
               </label>
             </div>
           </div>
@@ -790,7 +798,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-label"
                 htmlFor="user-send-notifications-to-email"
               >
-                {i18n.t("send_notifications_to_email")}
+                {I18NextService.i18n.t("send_notifications_to_email")}
               </label>
             </div>
           </div>
@@ -800,7 +808,7 @@ export class Settings extends Component<any, SettingsState> {
               {this.state.saveRes.state === "loading" ? (
                 <Spinner />
               ) : (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               )}
             </button>
           </div>
@@ -813,12 +821,12 @@ export class Settings extends Component<any, SettingsState> {
                 this.handleDeleteAccountShowConfirmToggle
               )}
             >
-              {i18n.t("delete_account")}
+              {I18NextService.i18n.t("delete_account")}
             </button>
             {this.state.deleteAccountShowConfirm && (
               <>
                 <div className="my-2 alert alert-danger" role="alert">
-                  {i18n.t("delete_account_confirm")}
+                  {I18NextService.i18n.t("delete_account_confirm")}
                 </div>
                 <input
                   type="password"
@@ -839,7 +847,7 @@ export class Settings extends Component<any, SettingsState> {
                   {this.state.deleteAccountRes.state === "loading" ? (
                     <Spinner />
                   ) : (
-                    capitalizeFirstLetter(i18n.t("delete"))
+                    capitalizeFirstLetter(I18NextService.i18n.t("delete"))
                   )}
                 </button>
                 <button
@@ -849,7 +857,7 @@ export class Settings extends Component<any, SettingsState> {
                     this.handleDeleteAccountShowConfirmToggle
                   )}
                 >
-                  {i18n.t("cancel")}
+                  {I18NextService.i18n.t("cancel")}
                 </button>
               </>
             )}
@@ -876,7 +884,7 @@ export class Settings extends Component<any, SettingsState> {
                 onChange={linkEvent(this, this.handleGenerateTotp)}
               />
               <label className="form-check-label" htmlFor="user-generate-totp">
-                {i18n.t("set_up_two_factor")}
+                {I18NextService.i18n.t("set_up_two_factor")}
               </label>
             </div>
           </div>
@@ -886,7 +894,7 @@ export class Settings extends Component<any, SettingsState> {
           <>
             <div>
               <a className="btn btn-secondary mb-2" href={totpUrl}>
-                {i18n.t("two_factor_link")}
+                {I18NextService.i18n.t("two_factor_link")}
               </a>
             </div>
             <div className="input-group mb-3">
@@ -901,7 +909,7 @@ export class Settings extends Component<any, SettingsState> {
                   onChange={linkEvent(this, this.handleRemoveTotp)}
                 />
                 <label className="form-check-label" htmlFor="user-remove-totp">
-                  {i18n.t("remove_two_factor")}
+                  {I18NextService.i18n.t("remove_two_factor")}
                 </label>
               </div>
             </div>
@@ -1050,7 +1058,7 @@ export class Settings extends Component<any, SettingsState> {
     // Coerce false to undefined here, so it won't generate it.
     const checked: boolean | undefined = event.target.checked || undefined;
     if (checked) {
-      toast(i18n.t("two_factor_setup_instructions"));
+      toast(I18NextService.i18n.t("two_factor_setup_instructions"));
     }
     i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
   }
@@ -1078,7 +1086,9 @@ export class Settings extends Component<any, SettingsState> {
 
   handleInterfaceLangChange(i: Settings, event: any) {
     const newLang = event.target.value ?? "browser";
-    i18n.changeLanguage(newLang === "browser" ? navigator.languages : newLang);
+    I18NextService.i18n.changeLanguage(
+      newLang === "browser" ? navigator.languages : newLang
+    );
 
     i.setState(
       s => ((s.saveUserSettingsForm.interface_language = event.target.value), s)
@@ -1168,7 +1178,7 @@ export class Settings extends Component<any, SettingsState> {
     if (saveRes.state === "success") {
       UserService.Instance.login(saveRes.data);
       location.reload();
-      toast(i18n.t("saved"));
+      toast(I18NextService.i18n.t("saved"));
       window.scrollTo(0, 0);
     }
 
@@ -1191,7 +1201,7 @@ export class Settings extends Component<any, SettingsState> {
       if (changePasswordRes.state === "success") {
         UserService.Instance.login(changePasswordRes.data);
         window.scrollTo(0, 0);
-        toast(i18n.t("password_changed"));
+        toast(I18NextService.i18n.t("password_changed"));
       }
 
       i.setState({ changePasswordRes });
index 7ef5382392e8f9ec059cc710c0c062163a1ebf7c..1800c3f25753fc255b3807591cde390ea34941a5 100644 (file)
@@ -1,7 +1,7 @@
 import { setIsoData } from "@utils/app";
 import { Component } from "inferno";
 import { GetSiteResponse, VerifyEmailResponse } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { HtmlTags } from "../common/html-tags";
@@ -36,7 +36,7 @@ export class VerifyEmail extends Component<any, State> {
     });
 
     if (this.state.verifyRes.state == "success") {
-      toast(i18n.t("email_verified"));
+      toast(I18NextService.i18n.t("email_verified"));
       this.props.history.push("/login");
     }
   }
@@ -46,7 +46,7 @@ export class VerifyEmail extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t("verify_email")} - ${
+    return `${I18NextService.i18n.t("verify_email")} - ${
       this.state.siteRes.site_view.site.name
     }`;
   }
@@ -60,7 +60,7 @@ export class VerifyEmail extends Component<any, State> {
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-            <h5>{i18n.t("verify_email")}</h5>
+            <h5>{I18NextService.i18n.t("verify_email")}</h5>
             {this.state.verifyRes.state == "loading" && (
               <h5>
                 <Spinner large />
index aa69038128c88abff8825f7f4494e96c4cab19c2..fe941830c6ab648ef9d05647568ef8081d2906ca 100644 (file)
@@ -11,9 +11,8 @@ import {
   GetSiteResponse,
   ListCommunitiesResponse,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest, PostFormParams } from "../../interfaces";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../../services";
 import {
   HttpService,
   RequestState,
@@ -143,7 +142,7 @@ export class CreatePost extends Component<
   }
 
   get documentTitle(): string {
-    return `${i18n.t("create_post")} - ${
+    return `${I18NextService.i18n.t("create_post")} - ${
       this.state.siteRes.site_view.site.name
     }`;
   }
@@ -171,7 +170,7 @@ export class CreatePost extends Component<
               id="createPostForm"
               className="col-12 col-lg-6 offset-lg-3 mb-4"
             >
-              <h1 className="h4">{i18n.t("create_post")}</h1>
+              <h1 className="h4">{I18NextService.i18n.t("create_post")}</h1>
               <PostForm
                 onCreate={this.handlePostCreate}
                 params={locationState}
index 16415d2dd78ceef885483a6393c0df5e6d8e7dd8..9ba0cbaf2ba112c2be9c2b1d7d22c3932ab9f556 100644 (file)
@@ -2,7 +2,7 @@ import { Component, linkEvent } from "inferno";
 import { Post } from "lemmy-js-client";
 import * as sanitizeHtml from "sanitize-html";
 import { relTags } from "../../config";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon } from "../common/icon";
 
 interface MetadataCardProps {
@@ -66,7 +66,7 @@ export class MetadataCard extends Component<
                       className="mt-2 btn btn-secondary text-monospace"
                       onClick={linkEvent(this, this.handleIframeExpand)}
                     >
-                      {i18n.t("expand_here")}
+                      {I18NextService.i18n.t("expand_here")}
                     </button>
                   )}
                 </div>
@@ -75,10 +75,14 @@ export class MetadataCard extends Component<
           </div>
         )}
         {this.state.expanded && post.embed_video_url && (
-          <iframe
-            className="post-metadata-iframe"
-            src={post.embed_video_url}
-          ></iframe>
+          <div className="ratio ratio-16x9">
+            <iframe
+              allowFullScreen
+              className="post-metadata-iframe"
+              src={post.embed_video_url}
+              title={post.embed_title}
+            ></iframe>
+          </div>
         )}
       </>
     );
index 9385179836ba6e320556a303b663ca0601364bb6..d2793fa2810e529f2c78e0be0dd8f9c058cec1de 100644 (file)
@@ -31,9 +31,8 @@ import {
   trendingFetchLimit,
   webArchiveUrl,
 } from "../../config";
-import { i18n } from "../../i18next";
 import { PostFormParams } from "../../interfaces";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { setupTippy } from "../../tippy";
 import { toast } from "../../toast";
@@ -342,7 +341,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
         />
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="post-url">
-            {i18n.t("url")}
+            {I18NextService.i18n.t("url")}
           </label>
           <div className="col-sm-10">
             <input
@@ -360,7 +359,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 className={`${
                   UserService.Instance.myUserInfo && "pointer"
                 } d-inline-block float-right text-muted font-weight-bold`}
-                data-tippy-content={i18n.t("upload_image")}
+                data-tippy-content={I18NextService.i18n.t("upload_image")}
               >
                 <Icon icon="image" classes="icon-inline" />
               </label>
@@ -381,7 +380,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   className="me-2 d-inline-block float-right text-muted small font-weight-bold"
                   rel={relTags}
                 >
-                  archive.org {i18n.t("archive_link")}
+                  archive.org {I18NextService.i18n.t("archive_link")}
                 </a>
                 <a
                   href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
@@ -390,7 +389,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   className="me-2 d-inline-block float-right text-muted small font-weight-bold"
                   rel={relTags}
                 >
-                  ghostarchive.org {i18n.t("archive_link")}
+                  ghostarchive.org {I18NextService.i18n.t("archive_link")}
                 </a>
                 <a
                   href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
@@ -399,7 +398,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   className="me-2 d-inline-block float-right text-muted small font-weight-bold"
                   rel={relTags}
                 >
-                  archive.today {i18n.t("archive_link")}
+                  archive.today {I18NextService.i18n.t("archive_link")}
                 </a>
               </div>
             )}
@@ -411,17 +410,17 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               <button
                 className="btn btn-danger btn-sm mt-2"
                 onClick={linkEvent(this, handleImageDelete)}
-                aria-label={i18n.t("delete")}
-                data-tippy-content={i18n.t("delete")}
+                aria-label={I18NextService.i18n.t("delete")}
+                data-tippy-content={I18NextService.i18n.t("delete")}
               >
                 <Icon icon="x" classes="icon-inline me-1" />
-                {capitalizeFirstLetter(i18n.t("delete"))}
+                {capitalizeFirstLetter(I18NextService.i18n.t("delete"))}
               </button>
             )}
             {this.props.crossPosts && this.props.crossPosts.length > 0 && (
               <>
                 <div className="my-1 text-muted small font-weight-bold">
-                  {i18n.t("cross_posts")}
+                  {I18NextService.i18n.t("cross_posts")}
                 </div>
                 <PostListings
                   showCommunity
@@ -455,7 +454,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
         </div>
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label" htmlFor="post-title">
-            {i18n.t("title")}
+            {I18NextService.i18n.t("title")}
           </label>
           <div className="col-sm-10">
             <textarea
@@ -472,7 +471,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             />
             {!validTitle(this.state.form.name) && (
               <div className="invalid-feedback">
-                {i18n.t("invalid_post_title")}
+                {I18NextService.i18n.t("invalid_post_title")}
               </div>
             )}
             {this.renderSuggestedPosts()}
@@ -480,7 +479,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
         </div>
 
         <div className="mb-3 row">
-          <label className="col-sm-2 col-form-label">{i18n.t("body")}</label>
+          <label className="col-sm-2 col-form-label">
+            {I18NextService.i18n.t("body")}
+          </label>
           <div className="col-sm-10">
             <MarkdownTextArea
               initialContent={this.state.form.body}
@@ -501,7 +502,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
         {!this.props.post_view && (
           <div className="mb-3 row">
             <label className="col-sm-2 col-form-label" htmlFor="post-community">
-              {i18n.t("community")}
+              {I18NextService.i18n.t("community")}
             </label>
             <div className="col-sm-10">
               <SearchableSelect
@@ -509,7 +510,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 value={this.state.form.community_id}
                 options={[
                   {
-                    label: i18n.t("select_a_community"),
+                    label: I18NextService.i18n.t("select_a_community"),
                     value: "",
                     disabled: true,
                   } as Choice,
@@ -530,7 +531,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               checked={this.state.form.nsfw}
               onChange={linkEvent(this, handlePostNsfwChange)}
             />
-            <label className="form-check-label">{i18n.t("nsfw")}</label>
+            <label className="form-check-label">
+              {I18NextService.i18n.t("nsfw")}
+            </label>
           </div>
         )}
         <input
@@ -553,9 +556,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               {this.state.loading ? (
                 <Spinner />
               ) : this.props.post_view ? (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               ) : (
-                capitalizeFirstLetter(i18n.t("create"))
+                capitalizeFirstLetter(I18NextService.i18n.t("create"))
               )}
             </button>
             {this.props.post_view && (
@@ -564,7 +567,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 className="btn btn-secondary"
                 onClick={linkEvent(this, handleCancel)}
               >
-                {i18n.t("cancel")}
+                {I18NextService.i18n.t("cancel")}
               </button>
             )}
           </div>
@@ -590,7 +593,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 copySuggestedTitle
               )}
             >
-              {i18n.t("copy_suggested_title", { title: "" })} {suggestedTitle}
+              {I18NextService.i18n.t("copy_suggested_title", { title: "" })}{" "}
+              {suggestedTitle}
             </div>
           )
         );
@@ -610,7 +614,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
           suggestedPosts.length > 0 && (
             <>
               <div className="my-1 text-muted small font-weight-bold">
-                {i18n.t("related_posts")}
+                {I18NextService.i18n.t("related_posts")}
               </div>
               <PostListings
                 showCommunity
index 3c271659c748482f793366180251148edb412df0..d5ddc2f20e5f285d42d00bf23930120e7ac34b10 100644 (file)
@@ -1,6 +1,12 @@
 import { myAuthRequired, newVote, showScores } from "@utils/app";
 import { canShare, share } from "@utils/browser";
-import { futureDaysToUnixTime, hostname, numToSI } from "@utils/helpers";
+import { getExternalHost, getHttpBase } from "@utils/env";
+import {
+  capitalizeFirstLetter,
+  futureDaysToUnixTime,
+  hostname,
+  numToSI,
+} from "@utils/helpers";
 import { isImage, isVideo } from "@utils/media";
 import {
   amAdmin,
@@ -38,11 +44,9 @@ import {
   TransferCommunity,
 } from "lemmy-js-client";
 import { relTags } from "../../config";
-import { getExternalHost, getHttpBase } from "../../env";
-import { i18n } from "../../i18next";
 import { BanType, PostFormParams, PurgeType, VoteType } from "../../interfaces";
 import { mdNoImages, mdToHtml, mdToHtmlInline } from "../../markdown";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { setupTippy } from "../../tippy";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
 import { MomentTime } from "../common/moment-time";
@@ -235,25 +239,40 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   get img() {
-    return this.imageSrc ? (
-      <>
-        <div className="offset-sm-3 my-2 d-none d-sm-block">
-          <a href={this.imageSrc} className="d-inline-block">
-            <PictrsImage src={this.imageSrc} />
-          </a>
-        </div>
-        <div className="my-2 d-block d-sm-none">
-          <a
-            className="d-inline-block"
-            onClick={linkEvent(this, this.handleImageExpandClick)}
-          >
-            <PictrsImage src={this.imageSrc} />
-          </a>
+    if (this.imageSrc) {
+      return (
+        <>
+          <div className="offset-sm-3 my-2 d-none d-sm-block">
+            <a href={this.imageSrc} className="d-inline-block">
+              <PictrsImage src={this.imageSrc} />
+            </a>
+          </div>
+          <div className="my-2 d-block d-sm-none">
+            <a
+              className="d-inline-block"
+              onClick={linkEvent(this, this.handleImageExpandClick)}
+            >
+              <PictrsImage src={this.imageSrc} />
+            </a>
+          </div>
+        </>
+      );
+    }
+
+    const { post } = this.postView;
+    const { url } = post;
+
+    if (url && isVideo(url)) {
+      return (
+        <div className="embed-responsive mt-3">
+          <video muted controls className="embed-responsive-item col-12">
+            <source src={url} type="video/mp4" />
+          </video>
         </div>
-      </>
-    ) : (
-      <></>
-    );
+      );
+    }
+
+    return <></>;
   }
 
   imgThumb(src: string) {
@@ -298,9 +317,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         <a
           href={this.imageSrc}
           className="text-body d-inline-block position-relative mb-2"
-          data-tippy-content={i18n.t("expand_here")}
+          data-tippy-content={I18NextService.i18n.t("expand_here")}
           onClick={linkEvent(this, this.handleImageExpandClick)}
-          aria-label={i18n.t("expand_here")}
+          aria-label={I18NextService.i18n.t("expand_here")}
         >
           {this.imgThumb(this.imageSrc)}
           <Icon icon="image" classes="mini-overlay" />
@@ -321,17 +340,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     } else if (url) {
       if (!this.props.hideImage && isVideo(url)) {
         return (
-          <div className="embed-responsive embed-responsive-16by9">
-            <video
-              playsInline
-              muted
-              loop
-              controls
-              className="embed-responsive-item"
-            >
-              <source src={url} type="video/mp4" />
-            </video>
-          </div>
+          <a
+            className="text-body"
+            href={url}
+            title={url}
+            rel={relTags}
+            data-tippy-content={I18NextService.i18n.t("expand_here")}
+            onClick={linkEvent(this, this.handleImageExpandClick)}
+            aria-label={I18NextService.i18n.t("expand_here")}
+          >
+            <div className="thumbnail rounded bg-light d-flex justify-content-center">
+              <Icon icon="play" classes="d-flex align-items-center" />
+            </div>
+          </a>
         );
       } else {
         return (
@@ -347,7 +368,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         <Link
           className="text-body"
           to={`/post/${post.id}`}
-          title={i18n.t("comments")}
+          title={I18NextService.i18n.t("comments")}
         >
           <div className="thumbnail rounded bg-light d-flex justify-content-center">
             <Icon icon="message-square" classes="d-flex align-items-center" />
@@ -363,20 +384,25 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <span className="small">
         <PersonListing person={post_view.creator} />
         {this.creatorIsMod_ && (
-          <span className="mx-1 badge text-bg-light">{i18n.t("mod")}</span>
+          <span className="mx-1 badge text-bg-light">
+            {I18NextService.i18n.t("mod")}
+          </span>
         )}
         {this.creatorIsAdmin_ && (
-          <span className="mx-1 badge text-bg-light">{i18n.t("admin")}</span>
+          <span className="mx-1 badge text-bg-light">
+            {I18NextService.i18n.t("admin")}
+          </span>
         )}
         {post_view.creator.bot_account && (
           <span className="mx-1 badge text-bg-light">
-            {i18n.t("bot_account").toLowerCase()}
+            {I18NextService.i18n.t("bot_account").toLowerCase()}
           </span>
         )}
         {this.props.showCommunity && (
           <>
             {" "}
-            {i18n.t("to")} <CommunityLink community={post_view.community} />
+            {I18NextService.i18n.t("to")}{" "}
+            <CommunityLink community={post_view.community} />
           </>
         )}
         {post_view.post.language_id !== 0 && (
@@ -405,8 +431,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             this.postView.my_vote == 1 ? "text-info" : "text-muted"
           }`}
           onClick={linkEvent(this, this.handleUpvote)}
-          data-tippy-content={i18n.t("upvote")}
-          aria-label={i18n.t("upvote")}
+          data-tippy-content={I18NextService.i18n.t("upvote")}
+          aria-label={I18NextService.i18n.t("upvote")}
           aria-pressed={this.postView.my_vote === 1}
         >
           {this.state.upvoteLoading ? (
@@ -431,8 +457,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               this.postView.my_vote == -1 ? "text-danger" : "text-muted"
             }`}
             onClick={linkEvent(this, this.handleDownvote)}
-            data-tippy-content={i18n.t("downvote")}
-            aria-label={i18n.t("downvote")}
+            data-tippy-content={I18NextService.i18n.t("downvote")}
+            aria-label={I18NextService.i18n.t("downvote")}
             aria-pressed={this.postView.my_vote === -1}
           >
             {this.state.downvoteLoading ? (
@@ -456,7 +482,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             : "text-primary"
         }`}
         to={`/post/${post.id}`}
-        title={i18n.t("comments")}
+        title={I18NextService.i18n.t("comments")}
       >
         <span
           className="d-inline"
@@ -494,7 +520,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             (post.thumbnail_url && (
               <button
                 className="btn btn-sm text-monospace text-muted d-inline-block"
-                data-tippy-content={i18n.t("expand_here")}
+                data-tippy-content={I18NextService.i18n.t("expand_here")}
                 onClick={linkEvent(this, this.handleImageExpandClick)}
               >
                 <Icon
@@ -507,13 +533,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             ))}
           {post.removed && (
             <small className="ms-2 badge text-bg-secondary">
-              {i18n.t("removed")}
+              {I18NextService.i18n.t("removed")}
             </small>
           )}
           {post.deleted && (
             <small
               className="unselectable pointer ms-2 text-muted font-italic"
-              data-tippy-content={i18n.t("deleted")}
+              data-tippy-content={I18NextService.i18n.t("deleted")}
             >
               <Icon icon="trash" classes="icon-inline text-danger" />
             </small>
@@ -521,7 +547,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           {post.locked && (
             <small
               className="unselectable pointer ms-2 text-muted font-italic"
-              data-tippy-content={i18n.t("locked")}
+              data-tippy-content={I18NextService.i18n.t("locked")}
             >
               <Icon icon="lock" classes="icon-inline text-danger" />
             </small>
@@ -529,8 +555,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           {post.featured_community && (
             <small
               className="unselectable pointer ms-2 text-muted font-italic"
-              data-tippy-content={i18n.t("featured_in_community")}
-              aria-label={i18n.t("featured_in_community")}
+              data-tippy-content={I18NextService.i18n.t(
+                "featured_in_community"
+              )}
+              aria-label={I18NextService.i18n.t("featured_in_community")}
             >
               <Icon icon="pin" classes="icon-inline text-primary" />
             </small>
@@ -538,15 +566,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           {post.featured_local && (
             <small
               className="unselectable pointer ms-2 text-muted font-italic"
-              data-tippy-content={i18n.t("featured_in_local")}
-              aria-label={i18n.t("featured_in_local")}
+              data-tippy-content={I18NextService.i18n.t("featured_in_local")}
+              aria-label={I18NextService.i18n.t("featured_in_local")}
             >
               <Icon icon="pin" classes="icon-inline text-secondary" />
             </small>
           )}
           {post.nsfw && (
             <small className="ms-2 badge text-bg-danger">
-              {i18n.t("nsfw")}
+              {I18NextService.i18n.t("nsfw")}
             </small>
           )}
         </div>
@@ -580,7 +608,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return dupes && dupes.length > 0 ? (
       <ul className="list-inline mb-1 small text-muted">
         <>
-          <li className="list-inline-item me-2">{i18n.t("cross_posted_to")}</li>
+          <li className="list-inline-item me-2">
+            {I18NextService.i18n.t("cross_posted_to")}
+          </li>
           {dupes.map(pv => (
             <li key={pv.post.id} className="list-inline-item me-2">
               <Link to={`/post/${pv.post.id}`}>
@@ -615,7 +645,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         {!post.local && (
           <a
             className="btn btn-sm btn-animate text-muted py-0"
-            title={i18n.t("link")}
+            title={I18NextService.i18n.t("link")}
             href={post.ap_id}
           >
             <Icon icon="fedilink" inline />
@@ -674,11 +704,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           <button
             className="btn btn-sm btn-animate text-muted py-0 dropdown-toggle"
             onClick={linkEvent(this, this.handleShowAdvanced)}
-            data-tippy-content={i18n.t("more")}
+            data-tippy-content={I18NextService.i18n.t("more")}
             data-bs-toggle="dropdown"
             aria-expanded="false"
             aria-controls="advancedButtonsDropdown"
-            aria-label={i18n.t("more")}
+            aria-label={I18NextService.i18n.t("more")}
           >
             <Icon icon="more-vertical" inline />
           </button>
@@ -718,7 +748,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   get commentsButton() {
     const post_view = this.postView;
-    const title = i18n.t("number_of_comments", {
+    const title = I18NextService.i18n.t("number_of_comments", {
       count: Number(post_view.counts.comments),
       formattedCount: Number(post_view.counts.comments),
     });
@@ -734,7 +764,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         {post_view.counts.comments}
         {this.unreadCount && (
           <span className="text-muted fst-italic">
-            ({this.unreadCount} {i18n.t("new")})
+            ({this.unreadCount} {I18NextService.i18n.t("new")})
           </span>
         )}
       </Link>
@@ -762,7 +792,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             }`}
             {...tippy}
             onClick={linkEvent(this, this.handleUpvote)}
-            aria-label={i18n.t("upvote")}
+            aria-label={I18NextService.i18n.t("upvote")}
             aria-pressed={this.postView.my_vote === 1}
           >
             {this.state.upvoteLoading ? (
@@ -785,7 +815,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               }`}
               onClick={linkEvent(this, this.handleDownvote)}
               {...tippy}
-              aria-label={i18n.t("downvote")}
+              aria-label={I18NextService.i18n.t("downvote")}
               aria-pressed={this.postView.my_vote === -1}
             >
               {this.state.downvoteLoading ? (
@@ -813,7 +843,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   get saveButton() {
     const saved = this.postView.saved;
-    const label = saved ? i18n.t("unsave") : i18n.t("save");
+    const label = saved
+      ? I18NextService.i18n.t("unsave")
+      : I18NextService.i18n.t("save");
     return (
       <button
         className="btn btn-sm btn-animate text-muted py-0"
@@ -846,9 +878,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           key: "",
           search: "",
         }}
-        title={i18n.t("cross_post")}
-        data-tippy-content={i18n.t("cross_post")}
-        aria-label={i18n.t("cross_post")}
+        title={I18NextService.i18n.t("cross_post")}
+        data-tippy-content={I18NextService.i18n.t("cross_post")}
+        aria-label={I18NextService.i18n.t("cross_post")}
       >
         <Icon icon="copy" inline />
       </Link>
@@ -860,10 +892,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <button
         className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
         onClick={linkEvent(this, this.handleShowReportDialog)}
-        aria-label={i18n.t("show_report_dialog")}
+        aria-label={I18NextService.i18n.t("show_report_dialog")}
       >
         <Icon classes="me-1" icon="flag" inline />
-        {i18n.t("create_report")}
+        {I18NextService.i18n.t("create_report")}
       </button>
     );
   }
@@ -873,14 +905,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <button
         className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
         onClick={linkEvent(this, this.handleBlockPersonClick)}
-        aria-label={i18n.t("block_user")}
+        aria-label={I18NextService.i18n.t("block_user")}
       >
         {this.state.blockLoading ? (
           <Spinner />
         ) : (
           <Icon classes="me-1" icon="slash" inline />
         )}
-        {i18n.t("block_user")}
+        {I18NextService.i18n.t("block_user")}
       </button>
     );
   }
@@ -890,17 +922,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <button
         className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
         onClick={linkEvent(this, this.handleEditClick)}
-        aria-label={i18n.t("edit")}
+        aria-label={I18NextService.i18n.t("edit")}
       >
         <Icon classes="me-1" icon="edit" inline />
-        {i18n.t("edit")}
+        {I18NextService.i18n.t("edit")}
       </button>
     );
   }
 
   get deleteButton() {
     const deleted = this.postView.post.deleted;
-    const label = !deleted ? i18n.t("delete") : i18n.t("restore");
+    const label = !deleted
+      ? I18NextService.i18n.t("delete")
+      : I18NextService.i18n.t("restore");
     return (
       <button
         className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
@@ -928,8 +962,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <button
         className="btn btn-sm btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleViewSource)}
-        data-tippy-content={i18n.t("view_source")}
-        aria-label={i18n.t("view_source")}
+        data-tippy-content={I18NextService.i18n.t("view_source")}
+        aria-label={I18NextService.i18n.t("view_source")}
       >
         <Icon
           icon="file-text"
@@ -942,7 +976,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   get lockButton() {
     const locked = this.postView.post.locked;
-    const label = locked ? i18n.t("unlock") : i18n.t("lock");
+    const label = locked
+      ? I18NextService.i18n.t("unlock")
+      : I18NextService.i18n.t("lock");
     return (
       <button
         className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
@@ -958,7 +994,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               classes={classNames("me-1", { "text-danger": locked })}
               inline
             />
-            {label}
+            {capitalizeFirstLetter(label)}
           </>
         )}
       </button>
@@ -968,13 +1004,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get featureButtons() {
     const featuredCommunity = this.postView.post.featured_community;
     const labelCommunity = featuredCommunity
-      ? i18n.t("unfeature_from_community")
-      : i18n.t("feature_in_community");
+      ? I18NextService.i18n.t("unfeature_from_community")
+      : I18NextService.i18n.t("feature_in_community");
 
     const featuredLocal = this.postView.post.featured_local;
     const labelLocal = featuredLocal
-      ? i18n.t("unfeature_from_local")
-      : i18n.t("feature_in_local");
+      ? I18NextService.i18n.t("unfeature_from_local")
+      : I18NextService.i18n.t("feature_in_local");
     return (
       <>
         <li>
@@ -995,7 +1031,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   })}
                   inline
                 />
-                {i18n.t("community")}
+                {I18NextService.i18n.t("community")}
               </>
             )}
           </button>
@@ -1019,7 +1055,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     })}
                     inline
                   />
-                  {i18n.t("local")}
+                  {I18NextService.i18n.t("local")}
                 </>
               )}
             </button>
@@ -1043,9 +1079,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         {this.state.removeLoading ? (
           <Spinner />
         ) : !removed ? (
-          i18n.t("remove")
+          I18NextService.i18n.t("remove")
         ) : (
-          i18n.t("restore")
+          I18NextService.i18n.t("restore")
         )}
       </button>
     );
@@ -1070,9 +1106,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                       this,
                       this.handleModBanFromCommunityShow
                     )}
-                    aria-label={i18n.t("ban_from_community")}
+                    aria-label={I18NextService.i18n.t("ban_from_community")}
                   >
-                    {i18n.t("ban_from_community")}
+                    {I18NextService.i18n.t("ban_from_community")}
                   </button>
                 ) : (
                   <button
@@ -1081,9 +1117,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                       this,
                       this.handleModBanFromCommunitySubmit
                     )}
-                    aria-label={i18n.t("unban")}
+                    aria-label={I18NextService.i18n.t("unban")}
                   >
-                    {this.state.banLoading ? <Spinner /> : i18n.t("unban")}
+                    {this.state.banLoading ? (
+                      <Spinner />
+                    ) : (
+                      I18NextService.i18n.t("unban")
+                    )}
                   </button>
                 ))}
               {!post_view.creator_banned_from_community && (
@@ -1092,16 +1132,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   onClick={linkEvent(this, this.handleAddModToCommunity)}
                   aria-label={
                     this.creatorIsMod_
-                      ? i18n.t("remove_as_mod")
-                      : i18n.t("appoint_as_mod")
+                      ? I18NextService.i18n.t("remove_as_mod")
+                      : I18NextService.i18n.t("appoint_as_mod")
                   }
                 >
                   {this.state.addModLoading ? (
                     <Spinner />
                   ) : this.creatorIsMod_ ? (
-                    i18n.t("remove_as_mod")
+                    I18NextService.i18n.t("remove_as_mod")
                   ) : (
-                    i18n.t("appoint_as_mod")
+                    I18NextService.i18n.t("appoint_as_mod")
                   )}
                 </button>
               )}
@@ -1118,24 +1158,28 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   this,
                   this.handleShowConfirmTransferCommunity
                 )}
-                aria-label={i18n.t("transfer_community")}
+                aria-label={I18NextService.i18n.t("transfer_community")}
               >
-                {i18n.t("transfer_community")}
+                {I18NextService.i18n.t("transfer_community")}
               </button>
             ) : (
               <>
                 <button
                   className="d-inline-block me-1 btn btn-link btn-animate text-muted py-0"
-                  aria-label={i18n.t("are_you_sure")}
+                  aria-label={I18NextService.i18n.t("are_you_sure")}
                 >
-                  {i18n.t("are_you_sure")}
+                  {I18NextService.i18n.t("are_you_sure")}
                 </button>
                 <button
                   className="btn btn-link btn-animate text-muted py-0 d-inline-block me-1"
-                  aria-label={i18n.t("yes")}
+                  aria-label={I18NextService.i18n.t("yes")}
                   onClick={linkEvent(this, this.handleTransferCommunity)}
                 >
-                  {this.state.transferLoading ? <Spinner /> : i18n.t("yes")}
+                  {this.state.transferLoading ? (
+                    <Spinner />
+                  ) : (
+                    I18NextService.i18n.t("yes")
+                  )}
                 </button>
                 <button
                   className="btn btn-link btn-animate text-muted py-0 d-inline-block"
@@ -1143,9 +1187,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     this,
                     this.handleCancelShowConfirmTransferCommunity
                   )}
-                  aria-label={i18n.t("no")}
+                  aria-label={I18NextService.i18n.t("no")}
                 >
-                  {i18n.t("no")}
+                  {I18NextService.i18n.t("no")}
                 </button>
               </>
             ))}
@@ -1158,36 +1202,36 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     <button
                       className="btn btn-link btn-animate text-muted py-0"
                       onClick={linkEvent(this, this.handleModBanShow)}
-                      aria-label={i18n.t("ban_from_site")}
+                      aria-label={I18NextService.i18n.t("ban_from_site")}
                     >
-                      {i18n.t("ban_from_site")}
+                      {I18NextService.i18n.t("ban_from_site")}
                     </button>
                   ) : (
                     <button
                       className="btn btn-link btn-animate text-muted py-0"
                       onClick={linkEvent(this, this.handleModBanSubmit)}
-                      aria-label={i18n.t("unban_from_site")}
+                      aria-label={I18NextService.i18n.t("unban_from_site")}
                     >
                       {this.state.banLoading ? (
                         <Spinner />
                       ) : (
-                        i18n.t("unban_from_site")
+                        I18NextService.i18n.t("unban_from_site")
                       )}
                     </button>
                   )}
                   <button
                     className="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handlePurgePersonShow)}
-                    aria-label={i18n.t("purge_user")}
+                    aria-label={I18NextService.i18n.t("purge_user")}
                   >
-                    {i18n.t("purge_user")}
+                    {I18NextService.i18n.t("purge_user")}
                   </button>
                   <button
                     className="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handlePurgePostShow)}
-                    aria-label={i18n.t("purge_post")}
+                    aria-label={I18NextService.i18n.t("purge_post")}
                   >
-                    {i18n.t("purge_post")}
+                    {I18NextService.i18n.t("purge_post")}
                   </button>
                 </>
               )}
@@ -1197,16 +1241,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   onClick={linkEvent(this, this.handleAddAdmin)}
                   aria-label={
                     this.creatorIsAdmin_
-                      ? i18n.t("remove_as_admin")
-                      : i18n.t("appoint_as_admin")
+                      ? I18NextService.i18n.t("remove_as_admin")
+                      : I18NextService.i18n.t("appoint_as_admin")
                   }
                 >
                   {this.state.addAdminLoading ? (
                     <Spinner />
                   ) : this.creatorIsAdmin_ ? (
-                    i18n.t("remove_as_admin")
+                    I18NextService.i18n.t("remove_as_admin")
                   ) : (
-                    i18n.t("appoint_as_admin")
+                    I18NextService.i18n.t("appoint_as_admin")
                   )}
                 </button>
               )}
@@ -1221,8 +1265,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     const post = this.postView;
     const purgeTypeText =
       this.state.purgeType == PurgeType.Post
-        ? i18n.t("purge_post")
-        : `${i18n.t("purge")} ${post.creator.name}`;
+        ? I18NextService.i18n.t("purge_post")
+        : `${I18NextService.i18n.t("purge")} ${post.creator.name}`;
     return (
       <>
         {this.state.showRemoveDialog && (
@@ -1234,22 +1278,26 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               className="visually-hidden"
               htmlFor="post-listing-remove-reason"
             >
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id="post-listing-remove-reason"
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               value={this.state.removeReason}
               onInput={linkEvent(this, this.handleModRemoveReasonChange)}
             />
             <button
               type="submit"
               className="btn btn-secondary"
-              aria-label={i18n.t("remove_post")}
+              aria-label={I18NextService.i18n.t("remove_post")}
             >
-              {this.state.removeLoading ? <Spinner /> : i18n.t("remove_post")}
+              {this.state.removeLoading ? (
+                <Spinner />
+              ) : (
+                I18NextService.i18n.t("remove_post")
+              )}
             </button>
           </form>
         )}
@@ -1260,24 +1308,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 className="col-form-label"
                 htmlFor="post-listing-ban-reason"
               >
-                {i18n.t("reason")}
+                {I18NextService.i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id="post-listing-ban-reason"
                 className="form-control me-2"
-                placeholder={i18n.t("reason")}
+                placeholder={I18NextService.i18n.t("reason")}
                 value={this.state.banReason}
                 onInput={linkEvent(this, this.handleModBanReasonChange)}
               />
               <label className="col-form-label" htmlFor={`mod-ban-expires`}>
-                {i18n.t("expires")}
+                {I18NextService.i18n.t("expires")}
               </label>
               <input
                 type="number"
                 id={`mod-ban-expires`}
                 className="form-control me-2"
-                placeholder={i18n.t("number_of_days")}
+                placeholder={I18NextService.i18n.t("number_of_days")}
                 value={this.state.banExpireDays}
                 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
               />
@@ -1293,9 +1341,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   <label
                     className="form-check-label"
                     htmlFor="mod-ban-remove-data"
-                    title={i18n.t("remove_content_more")}
+                    title={I18NextService.i18n.t("remove_content_more")}
                   >
-                    {i18n.t("remove_content")}
+                    {I18NextService.i18n.t("remove_content")}
                   </label>
                 </div>
               </div>
@@ -1303,19 +1351,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             {/* TODO hold off on expires until later */}
             {/* <div class="mb-3 row"> */}
             {/*   <label class="col-form-label">Expires</label> */}
-            {/*   <input type="date" class="form-control me-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
+            {/*   <input type="date" class="form-control me-2" placeholder={I18NextService.i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
             {/* </div> */}
             <div className="mb-3 row">
               <button
                 type="submit"
                 className="btn btn-secondary"
-                aria-label={i18n.t("ban")}
+                aria-label={I18NextService.i18n.t("ban")}
               >
                 {this.state.banLoading ? (
                   <Spinner />
                 ) : (
                   <span>
-                    {i18n.t("ban")} {post.creator.name}
+                    {I18NextService.i18n.t("ban")} {post.creator.name}
                   </span>
                 )}
               </button>
@@ -1328,13 +1376,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             onSubmit={linkEvent(this, this.handleReportSubmit)}
           >
             <label className="visually-hidden" htmlFor="post-report-reason">
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id="post-report-reason"
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               required
               value={this.state.reportReason}
               onInput={linkEvent(this, this.handleReportReasonChange)}
@@ -1342,9 +1390,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             <button
               type="submit"
               className="btn btn-secondary"
-              aria-label={i18n.t("create_report")}
+              aria-label={I18NextService.i18n.t("create_report")}
             >
-              {this.state.reportLoading ? <Spinner /> : i18n.t("create_report")}
+              {this.state.reportLoading ? (
+                <Spinner />
+              ) : (
+                I18NextService.i18n.t("create_report")
+              )}
             </button>
           </form>
         )}
@@ -1355,13 +1407,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           >
             <PurgeWarning />
             <label className="visually-hidden" htmlFor="purge-reason">
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id="purge-reason"
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               value={this.state.purgeReason}
               onInput={linkEvent(this, this.handlePurgeReasonChange)}
             />
@@ -1557,10 +1609,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     const body = post.body;
 
     return body
-      ? `${i18n.t("cross_posted_from")} ${post.ap_id}\n\n${body.replace(
-          /^/gm,
-          "> "
-        )}`
+      ? `${I18NextService.i18n.t("cross_posted_from")} ${
+          post.ap_id
+        }\n\n${body.replace(/^/gm, "> ")}`
       : undefined;
   }
 
@@ -1822,17 +1873,17 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   get pointsTippy(): string {
-    const points = i18n.t("number_of_points", {
+    const points = I18NextService.i18n.t("number_of_points", {
       count: Number(this.postView.counts.score),
       formattedCount: Number(this.postView.counts.score),
     });
 
-    const upvotes = i18n.t("number_of_upvotes", {
+    const upvotes = I18NextService.i18n.t("number_of_upvotes", {
       count: Number(this.postView.counts.upvotes),
       formattedCount: Number(this.postView.counts.upvotes),
     });
 
-    const downvotes = i18n.t("number_of_downvotes", {
+    const downvotes = I18NextService.i18n.t("number_of_downvotes", {
       count: Number(this.postView.counts.downvotes),
       formattedCount: Number(this.postView.counts.downvotes),
     });
index 1b2e9e8666d6d281be12caf50c2f0efcbfbd7d16..1fdaeba2ca40f1aa1f45bbdae292f3c3a908d5cc 100644 (file)
@@ -21,7 +21,7 @@ import {
   SavePost,
   TransferCommunity,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { PostListing } from "./post-listing";
 
 interface PostListingsProps {
@@ -101,7 +101,7 @@ export class PostListings extends Component<PostListingsProps, any> {
           ))
         ) : (
           <>
-            <div>{i18n.t("no_posts")}</div>
+            <div>{I18NextService.i18n.t("no_posts")}</div>
             {this.props.showCommunity && (
               <T i18nKey="subscribe_to_communities">
                 #<Link to="/communities">#</Link>
index 8af525d0d283414ef6cb0c87f622464fc54f5212..9b2540be1d18f2d0012f2a1ae43080d1a2681e8d 100644 (file)
@@ -2,7 +2,7 @@ import { myAuthRequired } from "@utils/app";
 import { Component, InfernoNode, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { PostReportView, PostView, ResolvePostReport } from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { PersonListing } from "../person/person-listing";
 import { PostListing } from "./post-listing";
@@ -37,7 +37,7 @@ export class PostReport extends Component<PostReportProps, PostReportState> {
     const r = this.props.report;
     const resolver = r.resolver;
     const post = r.post;
-    const tippyContent = i18n.t(
+    const tippyContent = I18NextService.i18n.t(
       r.post_report.resolved ? "unresolve_report" : "resolve_report"
     );
 
@@ -89,10 +89,11 @@ export class PostReport extends Component<PostReportProps, PostReportState> {
           onTransferCommunity={() => {}}
         />
         <div>
-          {i18n.t("reporter")}: <PersonListing person={r.creator} />
+          {I18NextService.i18n.t("reporter")}:{" "}
+          <PersonListing person={r.creator} />
         </div>
         <div>
-          {i18n.t("reason")}: {r.post_report.reason}
+          {I18NextService.i18n.t("reason")}: {r.post_report.reason}
         </div>
         {resolver && (
           <div>
index 31fc2e7023c472b50853e35b755c24f2c12b15ad..f0aa3ff5bfc60b5a6bd40cdcff11cf0b551bc1da 100644 (file)
@@ -76,14 +76,12 @@ import {
   TransferCommunity,
 } from "lemmy-js-client";
 import { commentTreeMaxDepth } from "../../config";
-import { i18n } from "../../i18next";
 import {
   CommentNodeI,
   CommentViewType,
   InitialFetchRequest,
 } from "../../interfaces";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { setupTippy } from "../../tippy";
 import { toast } from "../../toast";
@@ -400,7 +398,7 @@ export class Post extends Component<any, PostState> {
                   className="btn btn-secondary d-inline-block mb-2 me-3"
                   onClick={linkEvent(this, this.handleShowSidebarMobile)}
                 >
-                  {i18n.t("sidebar")}{" "}
+                  {I18NextService.i18n.t("sidebar")}{" "}
                   <Icon
                     icon={
                       this.state.showSidebarMobile
@@ -438,7 +436,7 @@ export class Post extends Component<any, PostState> {
               this.state.commentSort === "Hot" && "active"
             }`}
           >
-            {i18n.t("hot")}
+            {I18NextService.i18n.t("hot")}
             <input
               type="radio"
               className="btn-check"
@@ -452,7 +450,7 @@ export class Post extends Component<any, PostState> {
               this.state.commentSort === "Top" && "active"
             }`}
           >
-            {i18n.t("top")}
+            {I18NextService.i18n.t("top")}
             <input
               type="radio"
               className="btn-check"
@@ -466,7 +464,7 @@ export class Post extends Component<any, PostState> {
               this.state.commentSort === "New" && "active"
             }`}
           >
-            {i18n.t("new")}
+            {I18NextService.i18n.t("new")}
             <input
               type="radio"
               className="btn-check"
@@ -480,7 +478,7 @@ export class Post extends Component<any, PostState> {
               this.state.commentSort === "Old" && "active"
             }`}
           >
-            {i18n.t("old")}
+            {I18NextService.i18n.t("old")}
             <input
               type="radio"
               className="btn-check"
@@ -496,7 +494,7 @@ export class Post extends Component<any, PostState> {
               this.state.commentViewType === CommentViewType.Flat && "active"
             }`}
           >
-            {i18n.t("chat")}
+            {I18NextService.i18n.t("chat")}
             <input
               type="radio"
               className="btn-check"
@@ -595,14 +593,14 @@ export class Post extends Component<any, PostState> {
                 className="ps-0 d-block btn btn-link text-muted"
                 onClick={linkEvent(this, this.handleViewPost)}
               >
-                {i18n.t("view_all_comments")} ➔
+                {I18NextService.i18n.t("view_all_comments")} ➔
               </button>
               {showContextButton && (
                 <button
                   className="ps-0 d-block btn btn-link text-muted"
                   onClick={linkEvent(this, this.handleViewContext)}
                 >
-                  {i18n.t("show_context")} ➔
+                  {I18NextService.i18n.t("show_context")} ➔
                 </button>
               )}
             </>
@@ -836,14 +834,14 @@ export class Post extends Component<any, PostState> {
   async handleCommentReport(form: CreateCommentReport) {
     const reportRes = await HttpService.client.createCommentReport(form);
     if (reportRes.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
   async handlePostReport(form: CreatePostReport) {
     const reportRes = await HttpService.client.createPostReport(form);
     if (reportRes.state == "success") {
-      toast(i18n.t("report_created"));
+      toast(I18NextService.i18n.t("report_created"));
     }
   }
 
@@ -982,7 +980,7 @@ export class Post extends Component<any, PostState> {
 
   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
     if (purgeRes.state == "success") {
-      toast(i18n.t("purge_success"));
+      toast(I18NextService.i18n.t("purge_success"));
       this.context.router.history.push(`/`);
     }
   }
index 0bc704cf78601f3b6fc643ac4fdf2ebe044e707a..8afd3488425edec8641bc9545b3fd5b0f11a6411 100644 (file)
@@ -7,9 +7,8 @@ import {
   GetPersonDetailsResponse,
   GetSiteResponse,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import { toast } from "../../toast";
 import { HtmlTags } from "../common/html-tags";
@@ -97,7 +96,7 @@ export class CreatePrivateMessage extends Component<
   get documentTitle(): string {
     if (this.state.recipientRes.state == "success") {
       const name_ = this.state.recipientRes.data.person_view.person.name;
-      return `${i18n.t("create_private_message")} - ${name_}`;
+      return `${I18NextService.i18n.t("create_private_message")} - ${name_}`;
     } else {
       return "";
     }
@@ -116,7 +115,7 @@ export class CreatePrivateMessage extends Component<
         return (
           <div className="row">
             <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-              <h5>{i18n.t("create_private_message")}</h5>
+              <h5>{I18NextService.i18n.t("create_private_message")}</h5>
               <PrivateMessageForm
                 onCreate={this.handlePrivateMessageCreate}
                 recipient={res.person_view.person}
@@ -144,7 +143,7 @@ export class CreatePrivateMessage extends Component<
     const res = await HttpService.client.createPrivateMessage(form);
 
     if (res.state == "success") {
-      toast(i18n.t("message_sent"));
+      toast(I18NextService.i18n.t("message_sent"));
 
       // Navigate to the front
       this.context.router.history.push("/");
index 1b9cb50c74c79c4b4758171ae0e4b73e46e59a7e..338584b393ea3e6c1aa077c7dc2b0c83bd679719 100644 (file)
@@ -9,7 +9,7 @@ import {
   PrivateMessageView,
 } from "lemmy-js-client";
 import { relTags } from "../../config";
-import { i18n } from "../../i18next";
+import { I18NextService } from "../../services";
 import { setupTippy } from "../../tippy";
 import { Icon, Spinner } from "../common/icon";
 import { MarkdownTextArea } from "../common/markdown-textarea";
@@ -66,7 +66,7 @@ export class PrivateMessageForm extends Component<
   // TODO
   // <Prompt
   //   when={!this.state.loading && this.state.content}
-  //   message={i18n.t("block_leaving")}
+  //   message={I18NextService.i18n.t("block_leaving")}
   // />
 
   render() {
@@ -83,7 +83,7 @@ export class PrivateMessageForm extends Component<
         {!this.props.privateMessageView && (
           <div className="mb-3 row">
             <label className="col-sm-2 col-form-label">
-              {capitalizeFirstLetter(i18n.t("to"))}
+              {capitalizeFirstLetter(I18NextService.i18n.t("to"))}
             </label>
 
             <div className="col-sm-10 form-control-plaintext">
@@ -93,12 +93,14 @@ export class PrivateMessageForm extends Component<
         )}
         <div className="mb-3 row">
           <label className="col-sm-2 col-form-label">
-            {i18n.t("message")}
+            {I18NextService.i18n.t("message")}
             <button
               className="btn btn-link text-warning d-inline-block"
               onClick={linkEvent(this, this.handleShowDisclaimer)}
-              data-tippy-content={i18n.t("private_message_disclaimer")}
-              aria-label={i18n.t("private_message_disclaimer")}
+              data-tippy-content={I18NextService.i18n.t(
+                "private_message_disclaimer"
+              )}
+              aria-label={I18NextService.i18n.t("private_message_disclaimer")}
             >
               <Icon icon="alert-triangle" classes="icon-inline" />
             </button>
@@ -142,9 +144,9 @@ export class PrivateMessageForm extends Component<
               {this.state.loading ? (
                 <Spinner />
               ) : this.props.privateMessageView ? (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               ) : (
-                capitalizeFirstLetter(i18n.t("send_message"))
+                capitalizeFirstLetter(I18NextService.i18n.t("send_message"))
               )}
             </button>
             {this.props.privateMessageView && (
@@ -153,7 +155,7 @@ export class PrivateMessageForm extends Component<
                 className="btn btn-secondary"
                 onClick={linkEvent(this, this.handleCancel)}
               >
-                {i18n.t("cancel")}
+                {I18NextService.i18n.t("cancel")}
               </button>
             )}
             <ul className="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
index 38a20d857755ab615cfcae2c14dfdc13855d78a7..d15f18c5280587ced80617b5c4da0b5f12d93355 100644 (file)
@@ -5,8 +5,8 @@ import {
   PrivateMessageReportView,
   ResolvePrivateMessageReport,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { PersonListing } from "../person/person-listing";
 
@@ -39,28 +39,29 @@ export class PrivateMessageReport extends Component<Props, State> {
   render() {
     const r = this.props.report;
     const pmr = r.private_message_report;
-    const tippyContent = i18n.t(
+    const tippyContent = I18NextService.i18n.t(
       r.private_message_report.resolved ? "unresolve_report" : "resolve_report"
     );
 
     return (
       <div className="private-message-report">
         <div>
-          {i18n.t("creator")}:{" "}
+          {I18NextService.i18n.t("creator")}:{" "}
           <PersonListing person={r.private_message_creator} />
         </div>
         <div>
-          {i18n.t("message")}:
+          {I18NextService.i18n.t("message")}:
           <div
             className="md-div"
             dangerouslySetInnerHTML={mdToHtml(pmr.original_pm_text)}
           />
         </div>
         <div>
-          {i18n.t("reporter")}: <PersonListing person={r.creator} />
+          {I18NextService.i18n.t("reporter")}:{" "}
+          <PersonListing person={r.creator} />
         </div>
         <div>
-          {i18n.t("reason")}: {pmr.reason}
+          {I18NextService.i18n.t("reason")}: {pmr.reason}
         </div>
         {r.resolver && (
           <div>
index db40604c3fac3455d8a0d3ab7f52eba0d155504b..af8d64e5110ff64d0f65de8f7f0c57c288380de5 100644 (file)
@@ -9,9 +9,8 @@ import {
   Person,
   PrivateMessageView,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { mdToHtml } from "../../markdown";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { MomentTime } from "../common/moment-time";
 import { PersonListing } from "../person/person-listing";
@@ -94,7 +93,9 @@ export class PrivateMessage extends Component<
           <ul className="list-inline mb-0 text-muted small">
             {/* TODO refactor this */}
             <li className="list-inline-item">
-              {this.mine ? i18n.t("to") : i18n.t("from")}
+              {this.mine
+                ? I18NextService.i18n.t("to")
+                : I18NextService.i18n.t("from")}
             </li>
             <li className="list-inline-item">
               <PersonListing person={otherPerson} />
@@ -148,13 +149,13 @@ export class PrivateMessage extends Component<
                         onClick={linkEvent(this, this.handleMarkRead)}
                         data-tippy-content={
                           message_view.private_message.read
-                            ? i18n.t("mark_as_unread")
-                            : i18n.t("mark_as_read")
+                            ? I18NextService.i18n.t("mark_as_unread")
+                            : I18NextService.i18n.t("mark_as_read")
                         }
                         aria-label={
                           message_view.private_message.read
-                            ? i18n.t("mark_as_unread")
-                            : i18n.t("mark_as_read")
+                            ? I18NextService.i18n.t("mark_as_unread")
+                            : I18NextService.i18n.t("mark_as_read")
                         }
                       >
                         {this.state.readLoading ? (
@@ -175,8 +176,8 @@ export class PrivateMessage extends Component<
                       <button
                         className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleReplyClick)}
-                        data-tippy-content={i18n.t("reply")}
-                        aria-label={i18n.t("reply")}
+                        data-tippy-content={I18NextService.i18n.t("reply")}
+                        aria-label={I18NextService.i18n.t("reply")}
                       >
                         <Icon icon="reply1" classes="icon-inline" />
                       </button>
@@ -189,8 +190,8 @@ export class PrivateMessage extends Component<
                       <button
                         className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleEditClick)}
-                        data-tippy-content={i18n.t("edit")}
-                        aria-label={i18n.t("edit")}
+                        data-tippy-content={I18NextService.i18n.t("edit")}
+                        aria-label={I18NextService.i18n.t("edit")}
                       >
                         <Icon icon="edit" classes="icon-inline" />
                       </button>
@@ -201,13 +202,13 @@ export class PrivateMessage extends Component<
                         onClick={linkEvent(this, this.handleDeleteClick)}
                         data-tippy-content={
                           !message_view.private_message.deleted
-                            ? i18n.t("delete")
-                            : i18n.t("restore")
+                            ? I18NextService.i18n.t("delete")
+                            : I18NextService.i18n.t("restore")
                         }
                         aria-label={
                           !message_view.private_message.deleted
-                            ? i18n.t("delete")
-                            : i18n.t("restore")
+                            ? I18NextService.i18n.t("delete")
+                            : I18NextService.i18n.t("restore")
                         }
                       >
                         {this.state.deleteLoading ? (
@@ -229,8 +230,8 @@ export class PrivateMessage extends Component<
                   <button
                     className="btn btn-link btn-animate text-muted"
                     onClick={linkEvent(this, this.handleViewSource)}
-                    data-tippy-content={i18n.t("view_source")}
-                    aria-label={i18n.t("view_source")}
+                    data-tippy-content={I18NextService.i18n.t("view_source")}
+                    aria-label={I18NextService.i18n.t("view_source")}
                   >
                     <Icon
                       icon="file-text"
@@ -250,13 +251,13 @@ export class PrivateMessage extends Component<
             onSubmit={linkEvent(this, this.handleReportSubmit)}
           >
             <label className="visually-hidden" htmlFor="pm-report-reason">
-              {i18n.t("reason")}
+              {I18NextService.i18n.t("reason")}
             </label>
             <input
               type="text"
               id="pm-report-reason"
               className="form-control me-2"
-              placeholder={i18n.t("reason")}
+              placeholder={I18NextService.i18n.t("reason")}
               required
               value={this.state.reportReason}
               onInput={linkEvent(this, this.handleReportReasonChange)}
@@ -264,9 +265,13 @@ export class PrivateMessage extends Component<
             <button
               type="submit"
               className="btn btn-secondary"
-              aria-label={i18n.t("create_report")}
+              aria-label={I18NextService.i18n.t("create_report")}
             >
-              {this.state.reportLoading ? <Spinner /> : i18n.t("create_report")}
+              {this.state.reportLoading ? (
+                <Spinner />
+              ) : (
+                I18NextService.i18n.t("create_report")
+              )}
             </button>
           </form>
         )}
@@ -287,8 +292,8 @@ export class PrivateMessage extends Component<
       <button
         className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleShowReportDialog)}
-        data-tippy-content={i18n.t("show_report_dialog")}
-        aria-label={i18n.t("show_report_dialog")}
+        data-tippy-content={I18NextService.i18n.t("show_report_dialog")}
+        aria-label={I18NextService.i18n.t("show_report_dialog")}
       >
         <Icon icon="flag" inline />
       </button>
@@ -297,7 +302,9 @@ export class PrivateMessage extends Component<
 
   get messageUnlessRemoved(): string {
     const message = this.props.private_message_view.private_message;
-    return message.deleted ? `*${i18n.t("deleted")}*` : message.content;
+    return message.deleted
+      ? `*${I18NextService.i18n.t("deleted")}*`
+      : message.content;
   }
 
   handleReplyClick(i: PrivateMessage) {
index 72ea05a0a9c604ac6927aee0150db4e4df8a6673..473b18c483c430177a5a011dd825ece643f19387 100644 (file)
@@ -46,9 +46,8 @@ import {
   SortType,
 } from "lemmy-js-client";
 import { fetchLimit } from "../config";
-import { i18n } from "../i18next";
 import { CommentViewType, InitialFetchRequest } from "../interfaces";
-import { FirstLoadService } from "../services/FirstLoadService";
+import { FirstLoadService, I18NextService } from "../services";
 import { HttpService, RequestState } from "../services/HttpService";
 import { CommentNodes } from "./comment/comment-nodes";
 import { HtmlTags } from "./common/html-tags";
@@ -184,13 +183,13 @@ const Filter = ({
   return (
     <div className="mb-3 col-sm-6">
       <label className="col-form-label me-2" htmlFor={`${filterType}-filter`}>
-        {capitalizeFirstLetter(i18n.t(filterType))}
+        {capitalizeFirstLetter(I18NextService.i18n.t(filterType))}
       </label>
       <SearchableSelect
         id={`${filterType}-filter`}
         options={[
           {
-            label: i18n.t("all"),
+            label: I18NextService.i18n.t("all"),
             value: "0",
           },
         ].concat(options)}
@@ -228,7 +227,7 @@ function getListing(
   return (
     <>
       <span>{listing}</span>
-      <span>{` - ${i18n.t(translationKey, {
+      <span>{` - ${I18NextService.i18n.t(translationKey, {
         count: Number(count),
         formattedCount: numToSI(count),
       })}`}</span>
@@ -448,7 +447,7 @@ export class Search extends Component<any, SearchState> {
   get documentTitle(): string {
     const { q } = getSearchQueryParams();
     const name = this.state.siteRes.site_view.site.name;
-    return `${i18n.t("search")} - ${q ? `${q} - ` : ""}${name}`;
+    return `${I18NextService.i18n.t("search")} - ${q ? `${q} - ` : ""}${name}`;
   }
 
   render() {
@@ -460,13 +459,13 @@ export class Search extends Component<any, SearchState> {
           title={this.documentTitle}
           path={this.context.router.route.match.url}
         />
-        <h5>{i18n.t("search")}</h5>
+        <h5>{I18NextService.i18n.t("search")}</h5>
         {this.selects}
         {this.searchForm}
         {this.displayResults(type)}
         {this.resultsCount === 0 &&
           this.state.searchRes.state === "success" && (
-            <span>{i18n.t("no_results")}</span>
+            <span>{I18NextService.i18n.t("no_results")}</span>
           )}
         <Paginator page={page} onChange={this.handlePageChange} />
       </div>
@@ -499,8 +498,8 @@ export class Search extends Component<any, SearchState> {
             type="text"
             className="form-control me-2 mb-2 col-sm-8"
             value={this.state.searchText}
-            placeholder={`${i18n.t("search")}...`}
-            aria-label={i18n.t("search")}
+            placeholder={`${I18NextService.i18n.t("search")}...`}
+            aria-label={I18NextService.i18n.t("search")}
             onInput={linkEvent(this, this.handleQChange)}
             required
             minLength={1}
@@ -511,7 +510,7 @@ export class Search extends Component<any, SearchState> {
             {this.state.searchRes.state === "loading" ? (
               <Spinner />
             ) : (
-              <span>{i18n.t("search")}</span>
+              <span>{I18NextService.i18n.t("search")}</span>
             )}
           </button>
         </div>
@@ -540,14 +539,16 @@ export class Search extends Component<any, SearchState> {
           value={type}
           onChange={linkEvent(this, this.handleTypeChange)}
           className="form-select d-inline-block w-auto mb-2"
-          aria-label={i18n.t("type")}
+          aria-label={I18NextService.i18n.t("type")}
         >
           <option disabled aria-hidden="true">
-            {i18n.t("type")}
+            {I18NextService.i18n.t("type")}
           </option>
           {searchTypes.map(option => (
             <option value={option} key={option}>
-              {i18n.t(option.toString().toLowerCase() as NoOptionI18nKeys)}
+              {I18NextService.i18n.t(
+                option.toString().toLowerCase() as NoOptionI18nKeys
+              )}
             </option>
           ))}
         </select>
index 384e86c321236516dcdca6ebe739ab0433db1ebb..c56c64b0c2c3f1afce182f3598d1afebf432f1c5 100644 (file)
@@ -24,3 +24,15 @@ export const updateUnreadCountsInterval = 30000;
 export const fetchLimit = 40;
 export const relTags = "noopener nofollow";
 export const emDash = "\u2014";
+
+/**
+ * Accepted formats:
+ * !community@server.com
+ * /c/community@server.com
+ * /m/community@server.com
+ * /u/username@server.com
+ */
+export const instanceLinkRegex =
+  /(\/[cmu]\/|!)[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
+
+export const testHost = "0.0.0.0:8536";
diff --git a/src/shared/env.ts b/src/shared/env.ts
deleted file mode 100644 (file)
index 287912d..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-import { isBrowser } from "@utils/browser";
-
-const testHost = "0.0.0.0:8536";
-
-function getInternalHost() {
-  return !isBrowser()
-    ? process.env.LEMMY_UI_LEMMY_INTERNAL_HOST ?? testHost
-    : testHost; // used for local dev
-}
-
-export function getExternalHost() {
-  return isBrowser()
-    ? `${window.location.hostname}${
-        ["1234", "1235"].includes(window.location.port)
-          ? ":8536"
-          : window.location.port == ""
-          ? ""
-          : `:${window.location.port}`
-      }`
-    : process.env.LEMMY_UI_LEMMY_EXTERNAL_HOST || testHost;
-}
-
-function getSecure() {
-  return (
-    isBrowser()
-      ? window.location.protocol.includes("https")
-      : process.env.LEMMY_UI_HTTPS === "true"
-  )
-    ? "s"
-    : "";
-}
-
-function getHost() {
-  return isBrowser() ? getExternalHost() : getInternalHost();
-}
-
-function getBaseLocal(s = "") {
-  return `http${s}://${getHost()}`;
-}
-
-export function getHttpBaseInternal() {
-  return getBaseLocal(); // Don't use secure here
-}
-
-export function getHttpBaseExternal() {
-  return `http${getSecure()}://${getExternalHost()}`;
-}
-
-export function getHttpBase() {
-  return getBaseLocal(getSecure());
-}
-
-export function isHttps() {
-  return getSecure() === "s";
-}
-
-console.log(`httpbase: ${getHttpBase()}`);
-console.log(`isHttps: ${isHttps()}`);
-
-// This is for html tags, don't include port
-export function httpExternalPath(path: string) {
-  return `http${getSecure()}://${getExternalHost().replace(
-    /:\d+/g,
-    ""
-  )}${path}`;
-}
index d63dc8a6d345071523f36f8f5e63fd7794cd09a0..9f1ec733f74c52236f4b32cb03f24193ebf55009 100644 (file)
@@ -7,13 +7,14 @@ import emojiShortName from "emoji-short-name";
 import { CustomEmojiView } from "lemmy-js-client";
 import { default as MarkdownIt } from "markdown-it";
 import markdown_it_container from "markdown-it-container";
-import markdown_it_emoji from "markdown-it-emoji/bare";
+// import markdown_it_emoji from "markdown-it-emoji/bare";
 import markdown_it_footnote from "markdown-it-footnote";
 import markdown_it_html5_embed from "markdown-it-html5-embed";
 import markdown_it_sub from "markdown-it-sub";
 import markdown_it_sup from "markdown-it-sup";
 import Renderer from "markdown-it/lib/renderer";
 import Token from "markdown-it/lib/token";
+import { instanceLinkRegex } from "./config";
 
 export let Tribute: any;
 
@@ -72,6 +73,75 @@ const html5EmbedConfig = {
   },
 };
 
+function localInstanceLinkParser(md: MarkdownIt) {
+  md.core.ruler.push("replace-text", state => {
+    for (let i = 0; i < state.tokens.length; i++) {
+      if (state.tokens[i].type !== "inline") {
+        continue;
+      }
+      const inlineTokens: Token[] = state.tokens[i].children || [];
+      for (let j = inlineTokens.length - 1; j >= 0; j--) {
+        if (
+          inlineTokens[j].type === "text" &&
+          new RegExp(instanceLinkRegex).test(inlineTokens[j].content)
+        ) {
+          const text = inlineTokens[j].content;
+          const matches = Array.from(text.matchAll(instanceLinkRegex));
+
+          let lastIndex = 0;
+          const newTokens: Token[] = [];
+
+          let linkClass = "community-link";
+
+          for (const match of matches) {
+            // If there is plain text before the match, add it as a separate token
+            if (match.index !== undefined && match.index > lastIndex) {
+              const textToken = new state.Token("text", "", 0);
+              textToken.content = text.slice(lastIndex, match.index);
+              newTokens.push(textToken);
+            }
+
+            let href;
+            if (match[0].startsWith("!")) {
+              href = "/c/" + match[0].substring(1);
+            } else if (match[0].startsWith("/m/")) {
+              href = "/c/" + match[0].substring(3);
+            } else {
+              href = match[0];
+              if (match[0].startsWith("/u/")) {
+                linkClass = "user-link";
+              }
+            }
+
+            const linkOpenToken = new state.Token("link_open", "a", 1);
+            linkOpenToken.attrs = [
+              ["href", href],
+              ["class", linkClass],
+            ];
+            const textToken = new state.Token("text", "", 0);
+            textToken.content = match[0];
+            const linkCloseToken = new state.Token("link_close", "a", -1);
+
+            newTokens.push(linkOpenToken, textToken, linkCloseToken);
+
+            lastIndex =
+              (match.index !== undefined ? match.index : 0) + match[0].length;
+          }
+
+          // If there is plain text after the last match, add it as a separate token
+          if (lastIndex < text.length) {
+            const textToken = new state.Token("text", "", 0);
+            textToken.content = text.slice(lastIndex);
+            newTokens.push(textToken);
+          }
+
+          inlineTokens.splice(j, 1, ...newTokens);
+        }
+      }
+    }
+  });
+}
+
 export function setupMarkdown() {
   const markdownItConfig: MarkdownIt.Options = {
     html: false,
@@ -79,19 +149,20 @@ export function setupMarkdown() {
     typographer: true,
   };
 
-  const emojiDefs = Array.from(customEmojisLookup.entries()).reduce(
-    (main, [key, value]) => ({ ...main, [key]: value }),
-    {}
-  );
+  // const emojiDefs = Array.from(customEmojisLookup.entries()).reduce(
+  //   (main, [key, value]) => ({ ...main, [key]: value }),
+  //   {}
+  // );
   md = new MarkdownIt(markdownItConfig)
     .use(markdown_it_sub)
     .use(markdown_it_sup)
     .use(markdown_it_footnote)
     .use(markdown_it_html5_embed, html5EmbedConfig)
     .use(markdown_it_container, "spoiler", spoilerConfig)
-    .use(markdown_it_emoji, {
-      defs: emojiDefs,
-    });
+    .use(localInstanceLinkParser);
+  // .use(markdown_it_emoji, {
+  //   defs: emojiDefs,
+  // });
 
   mdNoImages = new MarkdownIt(markdownItConfig)
     .use(markdown_it_sub)
@@ -99,9 +170,10 @@ export function setupMarkdown() {
     .use(markdown_it_footnote)
     .use(markdown_it_html5_embed, html5EmbedConfig)
     .use(markdown_it_container, "spoiler", spoilerConfig)
-    .use(markdown_it_emoji, {
-      defs: emojiDefs,
-    })
+    .use(localInstanceLinkParser)
+    // .use(markdown_it_emoji, {
+    //   defs: emojiDefs,
+    // })
     .disable("image");
   const defaultRenderer = md.renderer.rules.image;
   md.renderer.rules.image = function (
index f6c30167d9843bfc4ac0f341050083d4daf1949f..361ffbd3f25fec27ce885b7a653f1c6355fe54b0 100644 (file)
@@ -1,7 +1,7 @@
+import { getHttpBase } from "@utils/env";
 import { LemmyHttp } from "lemmy-js-client";
-import { getHttpBase } from "../../shared/env";
-import { i18n } from "../../shared/i18next";
 import { toast } from "../../shared/toast";
+import { I18NextService } from "./I18NextService";
 
 type EmptyRequestState = {
   state: "empty";
@@ -62,7 +62,7 @@ class WrappedLemmyHttpClient {
             };
           } catch (error) {
             console.error(`API error: ${error}`);
-            toast(i18n.t(error), "danger");
+            toast(I18NextService.i18n.t(error), "danger");
             return {
               state: "failed",
               msg: error,
similarity index 53%
rename from src/shared/i18next.ts
rename to src/shared/services/I18NextService.ts
index aab43014b575026e4698ac5e16f9cf36ff9f7ba0..a7e8c979c017f707fcebc5bb61a65b1900974cd6 100644 (file)
@@ -1,37 +1,37 @@
 import { isBrowser } from "@utils/browser";
-import i18next, { i18nTyped, Resource } from "i18next";
-import { UserService } from "./services/UserService";
-import { ar } from "./translations/ar";
-import { bg } from "./translations/bg";
-import { ca } from "./translations/ca";
-import { cs } from "./translations/cs";
-import { da } from "./translations/da";
-import { de } from "./translations/de";
-import { el } from "./translations/el";
-import { en } from "./translations/en";
-import { eo } from "./translations/eo";
-import { es } from "./translations/es";
-import { eu } from "./translations/eu";
-import { fa } from "./translations/fa";
-import { fi } from "./translations/fi";
-import { fr } from "./translations/fr";
-import { ga } from "./translations/ga";
-import { gl } from "./translations/gl";
-import { hr } from "./translations/hr";
-import { id } from "./translations/id";
-import { it } from "./translations/it";
-import { ja } from "./translations/ja";
-import { ko } from "./translations/ko";
-import { nl } from "./translations/nl";
-import { oc } from "./translations/oc";
-import { pl } from "./translations/pl";
-import { pt } from "./translations/pt";
-import { pt_BR } from "./translations/pt_BR";
-import { ru } from "./translations/ru";
-import { sv } from "./translations/sv";
-import { vi } from "./translations/vi";
-import { zh } from "./translations/zh";
-import { zh_Hant } from "./translations/zh_Hant";
+import i18next, { Resource } from "i18next";
+import { UserService } from "../services";
+import { ar } from "../translations/ar";
+import { bg } from "../translations/bg";
+import { ca } from "../translations/ca";
+import { cs } from "../translations/cs";
+import { da } from "../translations/da";
+import { de } from "../translations/de";
+import { el } from "../translations/el";
+import { en } from "../translations/en";
+import { eo } from "../translations/eo";
+import { es } from "../translations/es";
+import { eu } from "../translations/eu";
+import { fa } from "../translations/fa";
+import { fi } from "../translations/fi";
+import { fr } from "../translations/fr";
+import { ga } from "../translations/ga";
+import { gl } from "../translations/gl";
+import { hr } from "../translations/hr";
+import { id } from "../translations/id";
+import { it } from "../translations/it";
+import { ja } from "../translations/ja";
+import { ko } from "../translations/ko";
+import { nl } from "../translations/nl";
+import { oc } from "../translations/oc";
+import { pl } from "../translations/pl";
+import { pt } from "../translations/pt";
+import { pt_BR } from "../translations/pt_BR";
+import { ru } from "../translations/ru";
+import { sv } from "../translations/sv";
+import { vi } from "../translations/vi";
+import { zh } from "../translations/zh";
+import { zh_Hant } from "../translations/zh_Hant";
 
 export const languages = [
   { resource: ar, code: "ar", name: "العربية" },
@@ -92,16 +92,30 @@ class LanguageDetector {
   }
 }
 
-i18next.use(LanguageDetector).init({
-  debug: false,
-  compatibilityJSON: "v3",
-  supportedLngs: languages.map(l => l.code),
-  nonExplicitSupportedLngs: true,
-  // load: 'languageOnly',
-  // initImmediate: false,
-  fallbackLng: "en",
-  resources,
-  interpolation: { format },
-});
+export class I18NextService {
+  #i18n: typeof i18next;
+  static #instance: I18NextService;
 
-export const i18n = i18next as i18nTyped;
+  private constructor() {
+    this.#i18n = i18next;
+    this.#i18n.use(LanguageDetector).init({
+      debug: false,
+      compatibilityJSON: "v3",
+      supportedLngs: languages.map(l => l.code),
+      nonExplicitSupportedLngs: true,
+      // load: 'languageOnly',
+      // initImmediate: false,
+      fallbackLng: "en",
+      resources,
+      interpolation: { format },
+    });
+  }
+
+  static get #Instance() {
+    return this.#instance ?? (this.#instance = new this());
+  }
+
+  public static get i18n() {
+    return this.#Instance.#i18n;
+  }
+}
index 61abc2ebff9bf8475649f3d15b1bd54d818f85f5..3d08e124d70cae526b97e1ed8295726011604134 100644 (file)
@@ -1,12 +1,12 @@
 // import Cookies from 'js-cookie';
 import { isAuthPath } from "@utils/app";
 import { isBrowser } from "@utils/browser";
+import { isHttps } from "@utils/env";
 import IsomorphicCookie from "isomorphic-cookie";
 import jwt_decode from "jwt-decode";
 import { LoginResponse, MyUserInfo } from "lemmy-js-client";
-import { isHttps } from "../env";
-import { i18n } from "../i18next";
 import { toast } from "../toast";
+import { I18NextService } from "./I18NextService";
 
 interface Claims {
   sub: number;
@@ -32,7 +32,7 @@ export class UserService {
     const expires = new Date();
     expires.setDate(expires.getDate() + 365);
     if (res.jwt) {
-      toast(i18n.t("logged_in"));
+      toast(I18NextService.i18n.t("logged_in"));
       IsomorphicCookie.save("jwt", res.jwt, { expires, secure: isHttps() });
       this.#setJwtInfo();
     }
@@ -58,7 +58,7 @@ export class UserService {
       const msg = "No JWT cookie found";
       if (throwErr && isBrowser()) {
         console.error(msg);
-        toast(i18n.t("not_logged_in"), "danger");
+        toast(I18NextService.i18n.t("not_logged_in"), "danger");
       }
       return undefined;
       // throw msg;
index f63a56be7be61b763dc5962bf4fda5721dde372f..5856245ac08a02475956c97d287a72be6abded88 100644 (file)
@@ -1,2 +1,5 @@
+export { FirstLoadService } from "./FirstLoadService";
+export { HistoryService } from "./HistoryService";
 export { HttpService } from "./HttpService";
+export { I18NextService } from "./I18NextService";
 export { UserService } from "./UserService";
index b8ab0623d2a6f138362fb19b28534c20d7ffcc7a..bd9b8a44477b4c9dbd240aaf47124adeb704240d 100644 (file)
@@ -1,7 +1,7 @@
 import { isBrowser } from "@utils/browser";
 import { ThemeColor } from "@utils/types";
 import Toastify from "toastify-js";
-import { i18n } from "./i18next";
+import { I18NextService } from "./services";
 
 export function toast(text: string, background: ThemeColor = "success") {
   if (isBrowser()) {
@@ -18,13 +18,18 @@ export function toast(text: string, background: ThemeColor = "success") {
 
 export function pictrsDeleteToast(filename: string, deleteUrl: string) {
   if (isBrowser()) {
-    const clickToDeleteText = i18n.t("click_to_delete_picture", { filename });
-    const deletePictureText = i18n.t("picture_deleted", {
+    const clickToDeleteText = I18NextService.i18n.t("click_to_delete_picture", {
       filename,
     });
-    const failedDeletePictureText = i18n.t("failed_to_delete_picture", {
+    const deletePictureText = I18NextService.i18n.t("picture_deleted", {
       filename,
     });
+    const failedDeletePictureText = I18NextService.i18n.t(
+      "failed_to_delete_picture",
+      {
+        filename,
+      }
+    );
 
     const backgroundColor = `var(--bs-light)`;
 
index 3a89a23c774a7c4fd34d9f175cf93add02da45b9..665ab46269aa99451b2a0e823829f8106a206f25 100644 (file)
@@ -5,6 +5,9 @@ export default function convertCommentSortType(
 ): CommentSortType {
   switch (sort) {
     case "TopAll":
+    case "TopHour":
+    case "TopSixHour":
+    case "TopTwelveHour":
     case "TopDay":
     case "TopWeek":
     case "TopMonth":
index 0f2a2dfe7f00b9d8f2992d3ca91c6c4c80cd21dd..ac6d9cb617b1858f39cc51c89d652992d4f48e23 100644 (file)
@@ -1,11 +1,10 @@
 import { GetSiteResponse } from "lemmy-js-client";
-import { i18n } from "../../i18next";
 import { setupEmojiDataModel, setupMarkdown } from "../../markdown";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 
 export default function initializeSite(site?: GetSiteResponse) {
   UserService.Instance.myUserInfo = site?.my_user;
-  i18n.changeLanguage();
+  I18NextService.i18n.changeLanguage();
   if (site) {
     setupEmojiDataModel(site.custom_emojis ?? []);
   }
index 7042527211be9ab5564cb2e8ca8fe742a1865392..9faa1ca41cee3ffbd60ade880ce95c4d0b0f0775 100644 (file)
@@ -1,6 +1,5 @@
 import { BlockCommunityResponse, MyUserInfo } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { toast } from "../../toast";
 
 export default function updateCommunityBlock(
@@ -13,12 +12,20 @@ export default function updateCommunityBlock(
         person: myUserInfo.local_user_view.person,
         community: data.community_view.community,
       });
-      toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
+      toast(
+        `${I18NextService.i18n.t("blocked")} ${
+          data.community_view.community.name
+        }`
+      );
     } else {
       myUserInfo.community_blocks = myUserInfo.community_blocks.filter(
         i => i.community.id !== data.community_view.community.id
       );
-      toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
+      toast(
+        `${I18NextService.i18n.t("unblocked")} ${
+          data.community_view.community.name
+        }`
+      );
     }
   }
 }
index 3b5223bc03d68ae2cc6246d5791e0f2d60186ce1..cec72a333745936fefd991d9b230eedf398eae4d 100644 (file)
@@ -1,6 +1,5 @@
 import { BlockPersonResponse, MyUserInfo } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { UserService } from "../../services";
+import { I18NextService, UserService } from "../../services";
 import { toast } from "../../toast";
 
 export default function updatePersonBlock(
@@ -13,12 +12,16 @@ export default function updatePersonBlock(
         person: myUserInfo.local_user_view.person,
         target: data.person_view.person,
       });
-      toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
+      toast(
+        `${I18NextService.i18n.t("blocked")} ${data.person_view.person.name}`
+      );
     } else {
       myUserInfo.person_blocks = myUserInfo.person_blocks.filter(
         i => i.target.id !== data.person_view.person.id
       );
-      toast(`${i18n.t("unblocked")} ${data.person_view.person.name}`);
+      toast(
+        `${I18NextService.i18n.t("unblocked")} ${data.person_view.person.name}`
+      );
     }
   }
 }
index f1534644d9c24a0cfc78ef9e2468d0068576c06f..6986ae597f1a6086784010e71beee7dc8ad01b44 100644 (file)
@@ -1,5 +1,6 @@
 export default function restoreScrollPosition(context: any) {
   const path: string = context.router.route.location.pathname;
   const y = Number(sessionStorage.getItem(`scrollPosition_${path}`));
+
   window.scrollTo(0, y);
 }
index 48353287dccfb4c1a7928ffb47713b9e3282590e..596d5d9f529eb0f378b2a8cbc9897990f0266eb4 100644 (file)
@@ -1,5 +1,6 @@
 export default function saveScrollPosition(context: any) {
   const path: string = context.router.route.location.pathname;
   const y = window.scrollY;
+
   sessionStorage.setItem(`scrollPosition_${path}`, y.toString());
 }
diff --git a/src/shared/utils/env/get-base-local.ts b/src/shared/utils/env/get-base-local.ts
new file mode 100644 (file)
index 0000000..754d66d
--- /dev/null
@@ -0,0 +1,5 @@
+import { getHost } from "@utils/env";
+
+export default function getBaseLocal(s = "") {
+  return `http${s}://${getHost()}`;
+}
diff --git a/src/shared/utils/env/get-external-host.ts b/src/shared/utils/env/get-external-host.ts
new file mode 100644 (file)
index 0000000..7e2635d
--- /dev/null
@@ -0,0 +1,14 @@
+import { isBrowser } from "@utils/browser";
+import { testHost } from "../../config";
+
+export default function getExternalHost() {
+  return isBrowser()
+    ? `${window.location.hostname}${
+        ["1234", "1235"].includes(window.location.port)
+          ? ":8536"
+          : window.location.port == ""
+          ? ""
+          : `:${window.location.port}`
+      }`
+    : process.env.LEMMY_UI_LEMMY_EXTERNAL_HOST || testHost;
+}
diff --git a/src/shared/utils/env/get-host.ts b/src/shared/utils/env/get-host.ts
new file mode 100644 (file)
index 0000000..0e361ac
--- /dev/null
@@ -0,0 +1,6 @@
+import { isBrowser } from "@utils/browser";
+import { getExternalHost, getInternalHost } from "@utils/env";
+
+export default function getHost() {
+  return isBrowser() ? getExternalHost() : getInternalHost();
+}
diff --git a/src/shared/utils/env/get-http-base-external.ts b/src/shared/utils/env/get-http-base-external.ts
new file mode 100644 (file)
index 0000000..c0ee3bb
--- /dev/null
@@ -0,0 +1,5 @@
+import { getExternalHost, getSecure } from "@utils/env";
+
+export default function getHttpBaseExternal() {
+  return `http${getSecure()}://${getExternalHost()}`;
+}
diff --git a/src/shared/utils/env/get-http-base-internal.ts b/src/shared/utils/env/get-http-base-internal.ts
new file mode 100644 (file)
index 0000000..e30c14c
--- /dev/null
@@ -0,0 +1,5 @@
+import { getBaseLocal } from "@utils/env";
+
+export default function getHttpBaseInternal() {
+  return getBaseLocal(); // Don't use secure here
+}
diff --git a/src/shared/utils/env/get-http-base.ts b/src/shared/utils/env/get-http-base.ts
new file mode 100644 (file)
index 0000000..97fa057
--- /dev/null
@@ -0,0 +1,5 @@
+import { getBaseLocal, getSecure } from "@utils/env";
+
+export default function getHttpBase() {
+  return getBaseLocal(getSecure());
+}
diff --git a/src/shared/utils/env/get-internal-host.ts b/src/shared/utils/env/get-internal-host.ts
new file mode 100644 (file)
index 0000000..ee8fdbd
--- /dev/null
@@ -0,0 +1,8 @@
+import { isBrowser } from "@utils/browser";
+import { testHost } from "../../config";
+
+export default function getInternalHost() {
+  return !isBrowser()
+    ? process.env.LEMMY_UI_LEMMY_INTERNAL_HOST ?? testHost
+    : testHost; // used for local dev
+}
diff --git a/src/shared/utils/env/get-secure.ts b/src/shared/utils/env/get-secure.ts
new file mode 100644 (file)
index 0000000..7e78b4f
--- /dev/null
@@ -0,0 +1,11 @@
+import { isBrowser } from "@utils/browser";
+
+export default function getSecure() {
+  return (
+    isBrowser()
+      ? window.location.protocol.includes("https")
+      : process.env.LEMMY_UI_HTTPS === "true"
+  )
+    ? "s"
+    : "";
+}
diff --git a/src/shared/utils/env/http-external-path.ts b/src/shared/utils/env/http-external-path.ts
new file mode 100644 (file)
index 0000000..f24ccac
--- /dev/null
@@ -0,0 +1,9 @@
+import { getExternalHost, getSecure } from "@utils/env";
+
+// This is for html tags, don't include port
+export default function httpExternalPath(path: string) {
+  return `http${getSecure()}://${getExternalHost().replace(
+    /:\d+/g,
+    ""
+  )}${path}`;
+}
diff --git a/src/shared/utils/env/index.ts b/src/shared/utils/env/index.ts
new file mode 100644 (file)
index 0000000..e14c673
--- /dev/null
@@ -0,0 +1,23 @@
+import getBaseLocal from "./get-base-local";
+import getExternalHost from "./get-external-host";
+import getHost from "./get-host";
+import getHttpBase from "./get-http-base";
+import getHttpBaseExternal from "./get-http-base-external";
+import getHttpBaseInternal from "./get-http-base-internal";
+import getInternalHost from "./get-internal-host";
+import getSecure from "./get-secure";
+import httpExternalPath from "./http-external-path";
+import isHttps from "./is-https";
+
+export {
+  getBaseLocal,
+  getExternalHost,
+  getHost,
+  getHttpBase,
+  getHttpBaseExternal,
+  getHttpBaseInternal,
+  getInternalHost,
+  getSecure,
+  httpExternalPath,
+  isHttps,
+};
diff --git a/src/shared/utils/env/is-https.ts b/src/shared/utils/env/is-https.ts
new file mode 100644 (file)
index 0000000..dd90757
--- /dev/null
@@ -0,0 +1,5 @@
+import { getSecure } from "@utils/env";
+
+export default function isHttps() {
+  return getSecure() === "s";
+}