mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
commit
55c8f77d3c
12 changed files with 221 additions and 40 deletions
|
@ -8,10 +8,14 @@
|
|||
"crypto-js": "^4.1.1",
|
||||
"fuse.js": "^6.4.6",
|
||||
"hls.js": "^1.0.7",
|
||||
"i18next": "^22.4.5",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-http-backend": "^2.1.0",
|
||||
"json5": "^2.2.0",
|
||||
"nanoid": "^4.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^12.1.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"srt-webvtt": "^2.0.0",
|
||||
"unpacker": "^1.0.1"
|
||||
|
|
46
public/locales/en-GB/translation.json
Normal file
46
public/locales/en-GB/translation.json
Normal file
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"global": {
|
||||
"name": "movie-web"
|
||||
},
|
||||
"search": {
|
||||
"loading": "Fetching your favourite shows...",
|
||||
"providersFailed": "{{fails}}/{{total}} providers failed!",
|
||||
"allResults": "That's all we have!",
|
||||
"noResults": "We couldn't find anything!",
|
||||
"allFailed": "All providers have failed!",
|
||||
"headingTitle": "Search results",
|
||||
"headingLink": "Back to home",
|
||||
"bookmarks": "Bookmarks",
|
||||
"continueWatching": "Continue Watching",
|
||||
"tagline": "Because watching legally is boring",
|
||||
"title": "What do you want to watch?",
|
||||
"placeholder": "What do you want to watch?"
|
||||
},
|
||||
"media": {
|
||||
"invalidUrl": "Your URL may be invalid",
|
||||
"arrowText": "Go back"
|
||||
},
|
||||
"notFound": {
|
||||
"backArrow": "Back to home",
|
||||
"media": {
|
||||
"title": "Couldn't find that media",
|
||||
"description": "We couldn't find the media you requested. Either it's been removed or you tampered with the URL"
|
||||
},
|
||||
"provider": {
|
||||
"title": "This provider has been disabled",
|
||||
"description": "We had issues with the provider or it was too unstable to use, so we had to disable it."
|
||||
},
|
||||
"page": {
|
||||
"title": "Couldn't find that page",
|
||||
"description": "We looked everywhere: under the bins, in the closet, behind the proxy but ultimately couldn't find the page you are looking for."
|
||||
}
|
||||
},
|
||||
"searchBar": {
|
||||
"movie": "Movie",
|
||||
"series": "Series",
|
||||
"Search": "Search"
|
||||
},
|
||||
"errorBoundary": {
|
||||
"text": "The app encountered an error and wasn't able to recover, please report it to the"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from "react";
|
||||
import { MWMediaType, MWQuery } from "@/providers";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DropdownButton } from "./buttons/DropdownButton";
|
||||
import { Icons } from "./Icon";
|
||||
import { TextInputControl } from "./text-inputs/TextInputControl";
|
||||
|
@ -13,6 +14,8 @@ export interface SearchBarProps {
|
|||
}
|
||||
|
||||
export function SearchBarInput(props: SearchBarProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
function setSearch(value: string) {
|
||||
props.onChange(
|
||||
|
@ -52,12 +55,12 @@ export function SearchBarInput(props: SearchBarProps) {
|
|||
options={[
|
||||
{
|
||||
id: MWMediaType.MOVIE,
|
||||
name: "Movie",
|
||||
name: t('searchBar.movie'),
|
||||
icon: Icons.FILM,
|
||||
},
|
||||
{
|
||||
id: MWMediaType.SERIES,
|
||||
name: "Series",
|
||||
name: t('searchBar.series'),
|
||||
icon: Icons.CLAPPER_BOARD,
|
||||
},
|
||||
// {
|
||||
|
@ -68,7 +71,7 @@ export function SearchBarInput(props: SearchBarProps) {
|
|||
]}
|
||||
onClick={() => setDropdownOpen((old) => !old)}
|
||||
>
|
||||
{props.buttonText || "Search"}
|
||||
{props.buttonText || t('searchBar.search')}
|
||||
</DropdownButton>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export function BrandPill(props: { clickable?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex items-center space-x-2 rounded-full bg-bink-100 bg-opacity-50 px-4 py-2 text-bink-600 ${
|
||||
props.clickable
|
||||
className={`flex items-center space-x-2 rounded-full bg-bink-100 bg-opacity-50 px-4 py-2 text-bink-600 ${props.clickable
|
||||
? "transition-[transform,background-color] hover:scale-105 hover:bg-bink-200 hover:text-bink-700 active:scale-95"
|
||||
: ""
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
<Icon className="text-xl" icon={Icons.MOVIE_WEB} />
|
||||
<span className="font-semibold text-white">movie-web</span>
|
||||
<span className="font-semibold text-white">{t('global.name')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,12 +14,15 @@ import {
|
|||
MWPortableMedia,
|
||||
} from "@/providers";
|
||||
import { getSeasonDataFromMedia } from "@/providers/methods/seasons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export interface SeasonsProps {
|
||||
media: MWMedia;
|
||||
}
|
||||
|
||||
export function LoadingSeasons(props: { error?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
|
@ -34,7 +37,7 @@ export function LoadingSeasons(props: { error?: boolean }) {
|
|||
) : (
|
||||
<div className="flex items-center space-x-3">
|
||||
<IconPatch icon={Icons.WARNING} className="text-red-400" />
|
||||
<p>Failed to load seasons and episodes</p>
|
||||
<p>{t('seasons.failed')}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -42,6 +45,8 @@ export function LoadingSeasons(props: { error?: boolean }) {
|
|||
}
|
||||
|
||||
export function Seasons(props: SeasonsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [searchSeasons, loading, error, success] = useLoading(
|
||||
(portableMedia: MWPortableMedia) => getSeasonDataFromMedia(portableMedia)
|
||||
);
|
||||
|
@ -70,7 +75,7 @@ export function Seasons(props: SeasonsProps) {
|
|||
|
||||
const mapSeason = (season: MWMediaSeason) => ({
|
||||
id: season.id,
|
||||
name: season.title || `Season ${season.sort}`,
|
||||
name: season.title || `${t('seasons.season')} ${season.sort}`,
|
||||
});
|
||||
|
||||
const options = seasons.seasons.map(mapSeason);
|
||||
|
|
28
src/i18n.ts
Normal file
28
src/i18n.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import Backend from 'i18next-http-backend';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
i18n
|
||||
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
|
||||
// learn more: https://github.com/i18next/i18next-http-backend
|
||||
// want your translations to be loaded from a professional CDN? => https://github.com/locize/react-tutorial#step-2---use-the-locize-cdn
|
||||
.use(Backend)
|
||||
// detect user language
|
||||
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
.use(LanguageDetector)
|
||||
// pass the i18n instance to react-i18next.
|
||||
.use(initReactI18next)
|
||||
// init i18next
|
||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
.init({
|
||||
fallbackLng: 'en-GB',
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default i18n;
|
|
@ -1,15 +1,18 @@
|
|||
import React from "react";
|
||||
import React, { Suspense } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { HashRouter } from "react-router-dom";
|
||||
import "./index.css";
|
||||
import { ErrorBoundary } from "@/components/layout/ErrorBoundary";
|
||||
import App from "./App";
|
||||
import './i18n';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<HashRouter>
|
||||
<App />
|
||||
<Suspense fallback="">
|
||||
<App />
|
||||
</Suspense>
|
||||
</HashRouter>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>,
|
||||
|
|
|
@ -100,8 +100,7 @@ export const xemovieScraper: MWMediaProvider = {
|
|||
const data = JSON.parse(
|
||||
JSON.stringify(
|
||||
eval(
|
||||
`(${
|
||||
script.textContent.replace("const data = ", "").split("};")[0]
|
||||
`(${script.textContent.replace("const data = ", "").split("};")[0]
|
||||
}})`
|
||||
)
|
||||
)
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
useBookmarkContext,
|
||||
} from "@/state/bookmark";
|
||||
import { getWatchedFromPortable, useWatchedContext } from "@/state/watched";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NotFoundChecks } from "./notfound/NotFoundChecks";
|
||||
|
||||
interface StyledMediaViewProps {
|
||||
|
@ -105,6 +106,8 @@ function StyledMediaFooter(props: StyledMediaFooterProps) {
|
|||
}
|
||||
|
||||
function LoadingMediaFooter(props: { error?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Paper className="mt-5">
|
||||
<div className="flex">
|
||||
|
@ -117,7 +120,7 @@ function LoadingMediaFooter(props: { error?: boolean }) {
|
|||
{props.error ? (
|
||||
<div className="flex items-center space-x-3">
|
||||
<IconPatch icon={Icons.WARNING} className="text-red-400" />
|
||||
<p>Your url may be invalid</p>
|
||||
<p>{t('media.invalidUrl')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<LoadingSeasons />
|
||||
|
@ -183,6 +186,7 @@ function MediaViewContent(props: { portable: MWPortableMedia }) {
|
|||
}
|
||||
|
||||
export function MediaView() {
|
||||
const { t } = useTranslation();
|
||||
const mediaPortable: MWPortableMedia | undefined = usePortableMedia();
|
||||
const reactHistory = useHistory();
|
||||
|
||||
|
@ -196,7 +200,7 @@ export function MediaView() {
|
|||
: reactHistory.push("/")
|
||||
}
|
||||
direction="left"
|
||||
linkText="Go back"
|
||||
linkText={t('media.arrowText')}
|
||||
/>
|
||||
</Navigation>
|
||||
<NotFoundChecks portable={mediaPortable}>
|
||||
|
|
|
@ -18,9 +18,11 @@ import {
|
|||
getIfBookmarkedFromPortable,
|
||||
useBookmarkContext,
|
||||
} from "@/state/bookmark/context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function SearchLoading() {
|
||||
return <Loading className="my-24" text="Fetching your favourite shows..." />;
|
||||
const { t } = useTranslation();
|
||||
return <Loading className="my-24" text={t('search.loading') || "Fetching your favourite shows..."} />;
|
||||
}
|
||||
|
||||
function SearchSuffix(props: {
|
||||
|
@ -28,6 +30,8 @@ function SearchSuffix(props: {
|
|||
total: number;
|
||||
resultsSize: number;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const allFailed: boolean = props.fails === props.total;
|
||||
const icon: Icons = allFailed ? Icons.WARNING : Icons.EYE_SLASH;
|
||||
|
||||
|
@ -43,13 +47,13 @@ function SearchSuffix(props: {
|
|||
<div>
|
||||
{props.fails > 0 ? (
|
||||
<p className="text-red-400">
|
||||
{props.fails}/{props.total} providers failed!
|
||||
{t('search.providersFailed', { fails: props.fails, total: props.total })}
|
||||
</p>
|
||||
) : null}
|
||||
{props.resultsSize > 0 ? (
|
||||
<p>That's all we have!</p>
|
||||
<p>{t('search.allResults')}</p>
|
||||
) : (
|
||||
<p>We couldn't find anything!</p>
|
||||
<p>{t('search.noResults')}</p>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -57,7 +61,7 @@ function SearchSuffix(props: {
|
|||
{/* Error result */}
|
||||
{allFailed ? (
|
||||
<div>
|
||||
<p>All providers have failed!</p>
|
||||
<p>{t('search.allFailed')}</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
@ -71,6 +75,8 @@ function SearchResultsView({
|
|||
searchQuery: MWQuery;
|
||||
clear: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [results, setResults] = useState<MWMassProviderOutput | undefined>();
|
||||
const [runSearchQuery, loading, error, success] = useLoading(
|
||||
(query: MWQuery) => SearchProviders(query)
|
||||
|
@ -91,9 +97,9 @@ function SearchResultsView({
|
|||
{/* results */}
|
||||
{success && results?.results.length ? (
|
||||
<SectionHeading
|
||||
title="Search results"
|
||||
title={t('search.headingTitle') || "Search results"}
|
||||
icon={Icons.SEARCH}
|
||||
linkText="Back to home"
|
||||
linkText={t('search.headingLink') || "Back to home"}
|
||||
onClick={() => clear()}
|
||||
>
|
||||
{results.results.map((v) => (
|
||||
|
@ -124,6 +130,8 @@ function SearchResultsView({
|
|||
}
|
||||
|
||||
function ExtraItems() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { getFilteredBookmarks } = useBookmarkContext();
|
||||
const { getFilteredWatched } = useWatchedContext();
|
||||
|
||||
|
@ -138,7 +146,7 @@ function ExtraItems() {
|
|||
return (
|
||||
<div className="mb-16 mt-32">
|
||||
{bookmarks.length > 0 ? (
|
||||
<SectionHeading title="Bookmarks" icon={Icons.BOOKMARK}>
|
||||
<SectionHeading title={t('search.bookmarks') || "Bookmarks"} icon={Icons.BOOKMARK}>
|
||||
{bookmarks.map((v) => (
|
||||
<WatchedMediaCard
|
||||
key={[v.mediaId, v.providerId].join("|")}
|
||||
|
@ -148,7 +156,7 @@ function ExtraItems() {
|
|||
</SectionHeading>
|
||||
) : null}
|
||||
{watchedItems.length > 0 ? (
|
||||
<SectionHeading title="Continue Watching" icon={Icons.CLOCK}>
|
||||
<SectionHeading title={t('search.continueWatching') || "Continue Watching"} icon={Icons.CLOCK}>
|
||||
{watchedItems.map((v) => (
|
||||
<WatchedMediaCard
|
||||
key={[v.mediaId, v.providerId].join("|")}
|
||||
|
@ -163,6 +171,8 @@ function ExtraItems() {
|
|||
}
|
||||
|
||||
export function SearchView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [searching, setSearching] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [search, setSearch, setSearchUnFocus] = useSearchQuery();
|
||||
|
@ -195,14 +205,14 @@ export function SearchView() {
|
|||
{/* input section */}
|
||||
<div className="mt-44 space-y-16 text-center">
|
||||
<div className="space-y-4">
|
||||
<Tagline>Because watching legally is boring</Tagline>
|
||||
<Title>What movie do you want to watch?</Title>
|
||||
<Tagline>{t('search.tagline')}</Tagline>
|
||||
<Title>{t('search.title')}</Title>
|
||||
</div>
|
||||
<SearchBarInput
|
||||
onChange={setSearch}
|
||||
value={search}
|
||||
onUnFocus={setSearchUnFocus}
|
||||
placeholder="What movie do you want to watch?"
|
||||
placeholder={t('search.placeholder') || "What do you want to watch?"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Icons } from "@/components/Icon";
|
|||
import { Navigation } from "@/components/layout/Navigation";
|
||||
import { ArrowLink } from "@/components/text/ArrowLink";
|
||||
import { Title } from "@/components/text/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function NotFoundWrapper(props: { children?: ReactNode }) {
|
||||
return (
|
||||
|
@ -17,52 +18,55 @@ function NotFoundWrapper(props: { children?: ReactNode }) {
|
|||
}
|
||||
|
||||
export function NotFoundMedia() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col items-center justify-center p-5 text-center">
|
||||
<IconPatch
|
||||
icon={Icons.EYE_SLASH}
|
||||
className="mb-6 text-xl text-bink-600"
|
||||
/>
|
||||
<Title>Couldn't find that media</Title>
|
||||
<Title>{t('notFound.media.title')}</Title>
|
||||
<p className="mt-5 mb-12 max-w-sm">
|
||||
We couldn't find the media you requested. Either it's been
|
||||
removed or you tampered with the URL
|
||||
{t('notFound.media.description')}
|
||||
</p>
|
||||
<ArrowLink to="/" linkText="Back to home" />
|
||||
<ArrowLink to="/" linkText={t('notFound.backArrow')} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NotFoundProvider() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col items-center justify-center p-5 text-center">
|
||||
<IconPatch
|
||||
icon={Icons.EYE_SLASH}
|
||||
className="mb-6 text-xl text-bink-600"
|
||||
/>
|
||||
<Title>This provider has been disabled</Title>
|
||||
<Title>{t('notFound.provider.title')}</Title>
|
||||
<p className="mt-5 mb-12 max-w-sm">
|
||||
We had issues with the provider or it was too unstable to use, so we had
|
||||
to disable it.
|
||||
{t('notFound.provider.description')}
|
||||
</p>
|
||||
<ArrowLink to="/" linkText="Back to home" />
|
||||
<ArrowLink to="/" linkText={t('notFound.backArrow')} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NotFoundPage() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<NotFoundWrapper>
|
||||
<IconPatch
|
||||
icon={Icons.EYE_SLASH}
|
||||
className="mb-6 text-xl text-bink-600"
|
||||
/>
|
||||
<Title>Couldn't find that page</Title>
|
||||
<Title>{t('notFound.page.title')}</Title>
|
||||
<p className="mt-5 mb-12 max-w-sm">
|
||||
We looked everywhere: under the bins, in the closet, behind the proxy
|
||||
but ultimately couldn't find the page you are looking for.
|
||||
{t('notFound.page.description')}
|
||||
</p>
|
||||
<ArrowLink to="/" linkText="Back to home" />
|
||||
<ArrowLink to="/" linkText={t('notFound.backArrow')} />
|
||||
</NotFoundWrapper>
|
||||
);
|
||||
}
|
||||
|
|
75
yarn.lock
75
yarn.lock
|
@ -10,7 +10,7 @@
|
|||
core-js-pure "^3.25.1"
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.18.9":
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.14.5", "@babel/runtime@^7.18.9", "@babel/runtime@^7.19.4", "@babel/runtime@^7.20.6":
|
||||
version "7.20.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
|
||||
integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
|
||||
|
@ -680,6 +680,13 @@ core-js-pure@^3.25.1:
|
|||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.26.1.tgz#653f4d7130c427820dcecd3168b594e8bb095a33"
|
||||
integrity sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==
|
||||
|
||||
cross-fetch@3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
|
||||
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
|
||||
dependencies:
|
||||
node-fetch "2.6.7"
|
||||
|
||||
cross-spawn@^7.0.2:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
|
@ -1354,6 +1361,34 @@ hoist-non-react-statics@^3.1.0:
|
|||
dependencies:
|
||||
react-is "^16.7.0"
|
||||
|
||||
html-parse-stringify@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
|
||||
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
|
||||
dependencies:
|
||||
void-elements "3.1.0"
|
||||
|
||||
i18next-browser-languagedetector@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz#ead34592edc96c6c3a618a51cb57ad027c5b5d87"
|
||||
integrity sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.19.4"
|
||||
|
||||
i18next-http-backend@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.1.0.tgz#c521603b864e5334fee21fcf1a57c5adefb15089"
|
||||
integrity sha512-rTVhhFrpnZJnNvCCdC6RjhFPk0S6mJ2VAix93vbDD19ixlrSJtoNqkk49wvR10PImBSsuGJf35gMQwn2mjer6A==
|
||||
dependencies:
|
||||
cross-fetch "3.1.5"
|
||||
|
||||
i18next@^22.4.5:
|
||||
version "22.4.5"
|
||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.5.tgz#7324e4946c2facbe743ca25bca8980af05b0a109"
|
||||
integrity sha512-Kc+Ow0guRetUq+kv02tj0Yof9zveROPBAmJ8UxxNODLVBRSwsM4iD0Gw3BEieOmkWemF6clU3K1fbnCuTqiN2Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.20.6"
|
||||
|
||||
ignore@^5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c"
|
||||
|
@ -1673,6 +1708,13 @@ natural-compare@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
node-fetch@2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-releases@^2.0.6:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.7.tgz#593edbc7c22860ee4d32d3933cfebdfab0c0e0e5"
|
||||
|
@ -1941,6 +1983,14 @@ react-dom@^17.0.2:
|
|||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-i18next@^12.1.1:
|
||||
version "12.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.1.1.tgz#2626cdbfe6bcb76ef833861c0184a5c4e5e3c089"
|
||||
integrity sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.14.5"
|
||||
html-parse-stringify "^3.0.1"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
@ -2251,6 +2301,11 @@ to-regex-range@^5.0.1:
|
|||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
tsconfig-paths@^3.14.1:
|
||||
version "3.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
|
||||
|
@ -2342,6 +2397,24 @@ vite@^4.0.1:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
void-elements@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
|
|
Loading…
Reference in a new issue