Compare commits
4 commits
main
...
frontend-i
Author | SHA1 | Date | |
---|---|---|---|
39fe66641a | |||
512cd33e3a | |||
656128a86a | |||
687a0016e9 |
71
frontend/package-lock.json
generated
71
frontend/package-lock.json
generated
|
@ -10,10 +10,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"flux": "^4.0.1",
|
"flux": "^4.0.1",
|
||||||
|
"i18next": "^20.3.3",
|
||||||
"mediacms-player": "file:packages/player",
|
"mediacms-player": "file:packages/player",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-i18next": "^11.11.3",
|
||||||
"sortablejs": "^1.13.0",
|
"sortablejs": "^1.13.0",
|
||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"url-parse": "^1.5.1"
|
"url-parse": "^1.5.1"
|
||||||
|
@ -8619,6 +8621,14 @@
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-parse-stringify": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||||
|
"dependencies": {
|
||||||
|
"void-elements": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/html-prettify": {
|
"node_modules/html-prettify": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/html-prettify/-/html-prettify-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/html-prettify/-/html-prettify-1.0.3.tgz",
|
||||||
|
@ -8752,6 +8762,14 @@
|
||||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/i18next": {
|
||||||
|
"version": "20.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.3.tgz",
|
||||||
|
"integrity": "sha512-tx9EUhHeaipvZ5pFLTaN9Xdm5Ssal774MpujaTA1Wv/ST/1My5SnoBmliY1lOpyEP5Z51Dq1gXifk/y4Yt3agQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
@ -13205,6 +13223,19 @@
|
||||||
"react": "17.0.2"
|
"react": "17.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-i18next": {
|
||||||
|
"version": "11.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.11.3.tgz",
|
||||||
|
"integrity": "sha512-upzG5/SpyOlYP5oSF4K8TZBvDWVhnCo38JNV+KnWjrg0+IaJCBltyh6lRGZDO5ovLyA4dU6Ip0bwbUCjb6Yyxw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.14.5",
|
||||||
|
"html-parse-stringify": "^3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"i18next": ">= 19.0.0",
|
||||||
|
"react": ">= 16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
@ -20404,6 +20435,14 @@
|
||||||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/void-elements": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
"integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
|
||||||
|
@ -21821,6 +21860,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/vjs-plugin": {
|
"packages/vjs-plugin": {
|
||||||
|
"name": "mediacms-vjs-plugin",
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -21854,6 +21894,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/vjs-plugin-font-icons": {
|
"packages/vjs-plugin-font-icons": {
|
||||||
|
"name": "mediacms-vjs-plugin-font-icons",
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -28693,6 +28734,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"html-parse-stringify": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||||
|
"requires": {
|
||||||
|
"void-elements": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"html-prettify": {
|
"html-prettify": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/html-prettify/-/html-prettify-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/html-prettify/-/html-prettify-1.0.3.tgz",
|
||||||
|
@ -28798,6 +28847,14 @@
|
||||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"i18next": {
|
||||||
|
"version": "20.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.3.tgz",
|
||||||
|
"integrity": "sha512-tx9EUhHeaipvZ5pFLTaN9Xdm5Ssal774MpujaTA1Wv/ST/1My5SnoBmliY1lOpyEP5Z51Dq1gXifk/y4Yt3agQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
@ -32276,6 +32333,15 @@
|
||||||
"scheduler": "^0.20.2"
|
"scheduler": "^0.20.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-i18next": {
|
||||||
|
"version": "11.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.11.3.tgz",
|
||||||
|
"integrity": "sha512-upzG5/SpyOlYP5oSF4K8TZBvDWVhnCo38JNV+KnWjrg0+IaJCBltyh6lRGZDO5ovLyA4dU6Ip0bwbUCjb6Yyxw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.14.5",
|
||||||
|
"html-parse-stringify": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-is": {
|
"react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
@ -38069,6 +38135,11 @@
|
||||||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"void-elements": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
"integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk="
|
||||||
|
},
|
||||||
"watchpack": {
|
"watchpack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
|
||||||
|
|
|
@ -42,10 +42,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"flux": "^4.0.1",
|
"flux": "^4.0.1",
|
||||||
|
"i18next": "^20.3.3",
|
||||||
"mediacms-player": "file:packages/player",
|
"mediacms-player": "file:packages/player",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-i18next": "^11.11.3",
|
||||||
"sortablejs": "^1.13.0",
|
"sortablejs": "^1.13.0",
|
||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"url-parse": "^1.5.1"
|
"url-parse": "^1.5.1"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
interface MediaListHeaderProps {
|
interface MediaListHeaderProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -9,7 +10,8 @@ interface MediaListHeaderProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MediaListHeader: React.FC<MediaListHeaderProps> = (props) => {
|
export const MediaListHeader: React.FC<MediaListHeaderProps> = (props) => {
|
||||||
const viewAllText = props.viewAllText || 'VIEW ALL';
|
const { t } = useTranslation();
|
||||||
|
const viewAllText = props.viewAllText || t('VIEW ALL');
|
||||||
return (
|
return (
|
||||||
<div className={(props.className ? props.className + ' ' : '') + 'media-list-header'} style={props.style}>
|
<div className={(props.className ? props.className + ' ' : '') + 'media-list-header'} style={props.style}>
|
||||||
<h2>{props.title}</h2>
|
<h2>{props.title}</h2>
|
||||||
|
|
|
@ -1,11 +1,49 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useLayout, usePopup } from '../../../utils/hooks/';
|
import { useLayout, usePopup } from '../../../utils/hooks/';
|
||||||
import { PageStore } from '../../../utils/stores/';
|
import { PageStore } from '../../../utils/stores/';
|
||||||
import { HeaderConsumer, MemberConsumer, LinksConsumer } from '../../../utils/contexts/';
|
import { HeaderConsumer, MemberConsumer, LinksConsumer } from '../../../utils/contexts/';
|
||||||
import { CircleIconButton, MaterialIcon, NavigationContentApp, NavigationMenuList, PopupTop, PopupMain, UserThumbnail } from '../../_shared';
|
import {
|
||||||
|
CircleIconButton,
|
||||||
|
MaterialIcon,
|
||||||
|
NavigationContentApp,
|
||||||
|
NavigationMenuList,
|
||||||
|
PopupTop,
|
||||||
|
PopupMain,
|
||||||
|
UserThumbnail,
|
||||||
|
} from '../../_shared';
|
||||||
import { HeaderThemeSwitcher } from './HeaderThemeSwitcher';
|
import { HeaderThemeSwitcher } from './HeaderThemeSwitcher';
|
||||||
|
import { LanguageOptions } from './LanguageOptions';
|
||||||
|
|
||||||
function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher) {
|
const OpenThemeSwitcher = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<CircleIconButton className="menu-item-icon change-page" data-page-id="main" aria-label="{t('Switch theme')}">
|
||||||
|
<i className="material-icons">arrow_back</i>
|
||||||
|
</CircleIconButton>
|
||||||
|
</span>
|
||||||
|
<span>{t('Switch theme')}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const OpenLanguageOptions = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<CircleIconButton className="menu-item-icon change-page" data-page-id="main" aria-label="Language">
|
||||||
|
<i className="material-icons">arrow_back</i>
|
||||||
|
</CircleIconButton>
|
||||||
|
</span>
|
||||||
|
<span>{t('Language')}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher, hasTranslations) {
|
||||||
const pages = {
|
const pages = {
|
||||||
main: null,
|
main: null,
|
||||||
};
|
};
|
||||||
|
@ -56,14 +94,7 @@ function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher) {
|
||||||
pages['switch-theme'] = (
|
pages['switch-theme'] = (
|
||||||
<div>
|
<div>
|
||||||
<PopupTop>
|
<PopupTop>
|
||||||
<div>
|
<OpenThemeSwitcher />
|
||||||
<span>
|
|
||||||
<CircleIconButton className="menu-item-icon change-page" data-page-id="main" aria-label="Switch theme">
|
|
||||||
<i className="material-icons">arrow_back</i>
|
|
||||||
</CircleIconButton>
|
|
||||||
</span>
|
|
||||||
<span>Switch theme</span>
|
|
||||||
</div>
|
|
||||||
</PopupTop>
|
</PopupTop>
|
||||||
<PopupMain>
|
<PopupMain>
|
||||||
<HeaderThemeSwitcher />
|
<HeaderThemeSwitcher />
|
||||||
|
@ -72,21 +103,37 @@ function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasTranslations) {
|
||||||
|
pages['language'] = (
|
||||||
|
<div>
|
||||||
|
<PopupTop>
|
||||||
|
<OpenLanguageOptions />
|
||||||
|
</PopupTop>
|
||||||
|
<PopupMain>
|
||||||
|
<LanguageOptions />
|
||||||
|
</PopupMain>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
function UploadMediaButton({ user, links }) {
|
function UploadMediaButton({ user, links }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
return !user.is.anonymous && user.can.addMedia ? (
|
return !user.is.anonymous && user.can.addMedia ? (
|
||||||
<div className={'hidden-only-in-small'}>
|
<div className={'hidden-only-in-small'}>
|
||||||
<CircleIconButton type="link" href={links.user.addMedia} title="Upload media">
|
<CircleIconButton type="link" href={links.user.addMedia} title="{t('Upload media')}">
|
||||||
<MaterialIcon type="video_call" />
|
<MaterialIcon type="video_call" />
|
||||||
<span className="hidden-txt">Upload media</span>
|
<span className="hidden-txt">{t('Upload media')}</span>
|
||||||
</CircleIconButton>
|
</CircleIconButton>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function LoginButton({ user, link, hasHeaderThemeSwitcher }) {
|
function LoginButton({ user, link, hasHeaderThemeSwitcher }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return user.is.anonymous && user.can.login ? (
|
return user.is.anonymous && user.can.login ? (
|
||||||
<div className="sign-in-wrap">
|
<div className="sign-in-wrap">
|
||||||
<a
|
<a
|
||||||
|
@ -95,15 +142,17 @@ function LoginButton({ user, link, hasHeaderThemeSwitcher }) {
|
||||||
className={
|
className={
|
||||||
'button-link sign-in' + (hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
|
'button-link sign-in' + (hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
|
||||||
}
|
}
|
||||||
title="Sign in"
|
title="{t('SIGN IN')}"
|
||||||
>
|
>
|
||||||
Sign in
|
{t('SIGN IN')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function RegisterButton({ user, link, hasHeaderThemeSwitcher }) {
|
function RegisterButton({ user, link, hasHeaderThemeSwitcher }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return user.is.anonymous && user.can.register ? (
|
return user.is.anonymous && user.can.register ? (
|
||||||
<div className="register-wrap">
|
<div className="register-wrap">
|
||||||
<a
|
<a
|
||||||
|
@ -112,9 +161,9 @@ function RegisterButton({ user, link, hasHeaderThemeSwitcher }) {
|
||||||
'button-link register-link' +
|
'button-link register-link' +
|
||||||
(hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
|
(hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
|
||||||
}
|
}
|
||||||
title="Register"
|
title="{t('REGISTER')}"
|
||||||
>
|
>
|
||||||
Register
|
{t('REGISTER')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
@ -144,7 +193,9 @@ export function HeaderRight(props) {
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
(user.is.anonymous ? 'user-options' : 'user-thumb') +
|
(user.is.anonymous ? 'user-options' : 'user-thumb') +
|
||||||
(!user.is.anonymous || header.hasThemeSwitcher ? '' : ' visible-only-in-extra-small')
|
(!user.is.anonymous || header.hasThemeSwitcher || header.hasTranslations
|
||||||
|
? ''
|
||||||
|
: ' visible-only-in-extra-small')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<PopupTrigger contentRef={popupContentRef}>
|
<PopupTrigger contentRef={popupContentRef}>
|
||||||
|
@ -160,7 +211,12 @@ export function HeaderRight(props) {
|
||||||
<PopupContent contentRef={popupContentRef}>
|
<PopupContent contentRef={popupContentRef}>
|
||||||
<NavigationContentApp
|
<NavigationContentApp
|
||||||
initPage="main"
|
initPage="main"
|
||||||
pages={headerPopupPages(user, header.popupNavItems, header.hasThemeSwitcher)}
|
pages={headerPopupPages(
|
||||||
|
user,
|
||||||
|
header.popupNavItems,
|
||||||
|
header.hasThemeSwitcher,
|
||||||
|
header.hasTranslations
|
||||||
|
)}
|
||||||
pageChangeSelector={'.change-page'}
|
pageChangeSelector={'.change-page'}
|
||||||
pageIdSelectorAttr={'data-page-id'}
|
pageIdSelectorAttr={'data-page-id'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
@import '../../../../css/includes/_variables.scss';
|
||||||
|
@import '../../../../css/includes/_variables_dimensions.scss';
|
||||||
|
|
||||||
|
.language-options {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-option {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 24px 0 64px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: inherit;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
background: 0;
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
margin-right: 16px;
|
||||||
|
color: var(--header-popup-menu-icon-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--in-popup-nav-menu-item-hover-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected-language {
|
||||||
|
button {
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { BrowserCache } from '../../../utils/classes';
|
||||||
|
import { SiteContext } from '../../../utils/contexts';
|
||||||
|
import { enabled as langEnabled, labels as langLabels } from '../../../utils/languages';
|
||||||
|
import { MaterialIcon } from '../../_shared';
|
||||||
|
|
||||||
|
import './LanguageOptions.scss';
|
||||||
|
|
||||||
|
interface LanguageOptionProps {
|
||||||
|
code: string;
|
||||||
|
label: string;
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LanguageOption: React.FC<LanguageOptionProps> = ({ code, label, active }) => {
|
||||||
|
const { i18n } = useTranslation();
|
||||||
|
const onClick = () => i18n.changeLanguage(code);
|
||||||
|
return (
|
||||||
|
<span className={'language-option' + (active ? ' selected-language' : '')}>
|
||||||
|
<button onClick={onClick}>
|
||||||
|
{active && <MaterialIcon type="check" />}
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LanguageOptions: React.FC = () => {
|
||||||
|
const site = useContext(SiteContext);
|
||||||
|
const [browserCache, setBrowserCache] = useState(null);
|
||||||
|
|
||||||
|
const { i18n } = useTranslation();
|
||||||
|
const [languages, setLanguages] = useState<string[]>([]);
|
||||||
|
const [current, setCurrent] = useState<string>(i18n.language);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
setBrowserCache(new BrowserCache('MediaCMS[' + site.id + '][language]', 86400));
|
||||||
|
}, [site.id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrent(i18n.language);
|
||||||
|
if (browserCache) {
|
||||||
|
// @ts-ignore
|
||||||
|
browserCache.set('code', i18n.language);
|
||||||
|
}
|
||||||
|
}, [i18n.language]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const lang: string[] = [];
|
||||||
|
for (let k in langEnabled) {
|
||||||
|
lang.push(langEnabled[k]);
|
||||||
|
}
|
||||||
|
setLanguages(lang);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="language-options">
|
||||||
|
{languages.map((code) => (
|
||||||
|
<LanguageOption key={code} code={code} label={langLabels[code] || code} active={code === current} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -196,7 +196,6 @@ $__button-link-horizontal-padding: 16;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
display: block;
|
display: block;
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.signin-icon-link {
|
.signin-icon-link {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import urlParse from 'url-parse';
|
import urlParse from 'url-parse';
|
||||||
import { useUser } from '../../../utils/hooks/';
|
import { useUser } from '../../../utils/hooks/';
|
||||||
import { PageStore } from '../../../utils/stores/';
|
import { PageStore } from '../../../utils/stores/';
|
||||||
|
@ -34,13 +35,15 @@ export function SidebarNavigationMenu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function MainMenuFirstSection() {
|
function MainMenuFirstSection() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const items = [];
|
const items = [];
|
||||||
|
|
||||||
if (!sidebar.hideHomeLink) {
|
if (!sidebar.hideHomeLink) {
|
||||||
items.push({
|
items.push({
|
||||||
link: links.home,
|
link: links.home,
|
||||||
icon: 'home',
|
icon: 'home',
|
||||||
text: 'Home',
|
text: t('Home'),
|
||||||
className: 'nav-item-home',
|
className: 'nav-item-home',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ApiUrlConsumer } from '../utils/contexts/';
|
import { ApiUrlConsumer } from '../utils/contexts/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||||
|
@ -9,18 +10,21 @@ interface CategoriesPageProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CategoriesPage: React.FC<CategoriesPageProps> = ({ id = 'categories', title = 'Categories' }) => (
|
export const CategoriesPage: React.FC<CategoriesPageProps> = ({ id = 'categories', title }) => {
|
||||||
<Page id={id}>
|
const { t } = useTranslation();
|
||||||
<ApiUrlConsumer>
|
return (
|
||||||
{(apiUrl) => (
|
<Page id={id}>
|
||||||
<MediaListWrapper title={title} className="items-list-ver">
|
<ApiUrlConsumer>
|
||||||
<LazyLoadItemListAsync
|
{(apiUrl) => (
|
||||||
singleLinkContent={true}
|
<MediaListWrapper title={title || t('Categories')} className="items-list-ver">
|
||||||
inCategoriesList={true}
|
<LazyLoadItemListAsync
|
||||||
requestUrl={apiUrl.archive.categories}
|
singleLinkContent={true}
|
||||||
/>
|
inCategoriesList={true}
|
||||||
</MediaListWrapper>
|
requestUrl={apiUrl.archive.categories}
|
||||||
)}
|
/>
|
||||||
</ApiUrlConsumer>
|
</MediaListWrapper>
|
||||||
</Page>
|
)}
|
||||||
);
|
</ApiUrlConsumer>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ApiUrlConsumer } from '../utils/contexts/';
|
import { ApiUrlConsumer } from '../utils/contexts/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||||
|
@ -9,14 +10,17 @@ interface MembersPageProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MembersPage: React.FC<MembersPageProps> = ({ id = 'members', title = 'Members' }) => (
|
export const MembersPage: React.FC<MembersPageProps> = ({ id = 'members', title }) => {
|
||||||
<Page id={id}>
|
const { t } = useTranslation();
|
||||||
<ApiUrlConsumer>
|
return (
|
||||||
{(apiUrl) => (
|
<Page id={id}>
|
||||||
<MediaListWrapper title={title} className="items-list-ver">
|
<ApiUrlConsumer>
|
||||||
<LazyLoadItemListAsync requestUrl={apiUrl.users} />
|
{(apiUrl) => (
|
||||||
</MediaListWrapper>
|
<MediaListWrapper title={title || t('Members')} className="items-list-ver">
|
||||||
)}
|
<LazyLoadItemListAsync requestUrl={apiUrl.users} />
|
||||||
</ApiUrlConsumer>
|
</MediaListWrapper>
|
||||||
</Page>
|
)}
|
||||||
);
|
</ApiUrlConsumer>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { withTranslation } from 'react-i18next';
|
||||||
import { ApiUrlConsumer } from '../utils/contexts/';
|
import { ApiUrlConsumer } from '../utils/contexts/';
|
||||||
import { PageStore } from '../utils/stores/';
|
import { PageStore } from '../utils/stores/';
|
||||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||||
|
@ -7,7 +8,7 @@ import ProfilePagesContent from '../components/profile-page/ProfilePagesContent'
|
||||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||||
import { ProfileMediaPage } from './ProfileMediaPage';
|
import { ProfileMediaPage } from './ProfileMediaPage';
|
||||||
|
|
||||||
export class ProfilePlaylistsPage extends ProfileMediaPage {
|
class ProfilePlaylistsPageClass extends ProfileMediaPage {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props, 'author-playlists');
|
super(props, 'author-playlists');
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ export class ProfilePlaylistsPage extends ProfileMediaPage {
|
||||||
<ApiUrlConsumer>
|
<ApiUrlConsumer>
|
||||||
{(apiUrl) => (
|
{(apiUrl) => (
|
||||||
<MediaListWrapper
|
<MediaListWrapper
|
||||||
title={-1 < this.state.playlistsCount ? 'Created playlists' : void 0}
|
title={-1 < this.state.playlistsCount ? this.props.t('Created playlists') : void 0}
|
||||||
className="profile-playlists-content items-list-ver"
|
className="profile-playlists-content items-list-ver"
|
||||||
>
|
>
|
||||||
<LazyLoadItemListAsync
|
<LazyLoadItemListAsync
|
||||||
|
@ -55,3 +56,7 @@ export class ProfilePlaylistsPage extends ProfileMediaPage {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProfilePlaylistsPage = withTranslation()(ProfilePlaylistsPageClass);
|
||||||
|
|
||||||
|
export { ProfilePlaylistsPage };
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
|
import i18next from 'i18next';
|
||||||
import { config as mediacmsConfig } from '../settings/config.js';
|
import { config as mediacmsConfig } from '../settings/config.js';
|
||||||
|
|
||||||
const config = mediacmsConfig(window.MediaCMS);
|
const config = mediacmsConfig(window.MediaCMS);
|
||||||
|
@ -8,6 +9,7 @@ const theme = config.theme;
|
||||||
const user = config.member;
|
const user = config.member;
|
||||||
|
|
||||||
const hasThemeSwitcher = theme.switch.enabled && 'header' === theme.switch.position;
|
const hasThemeSwitcher = theme.switch.enabled && 'header' === theme.switch.position;
|
||||||
|
const hasTranslations = true;
|
||||||
|
|
||||||
function popupTopNavItems() {
|
function popupTopNavItems() {
|
||||||
const items = [];
|
const items = [];
|
||||||
|
@ -17,7 +19,7 @@ function popupTopNavItems() {
|
||||||
items.push({
|
items.push({
|
||||||
link: links.user.addMedia,
|
link: links.user.addMedia,
|
||||||
icon: 'video_call',
|
icon: 'video_call',
|
||||||
text: 'Upload media',
|
text: i18next.t('Upload media'),
|
||||||
itemAttr: {
|
itemAttr: {
|
||||||
className: 'visible-only-in-small',
|
className: 'visible-only-in-small',
|
||||||
},
|
},
|
||||||
|
@ -27,7 +29,7 @@ function popupTopNavItems() {
|
||||||
items.push({
|
items.push({
|
||||||
link: user.pages.media,
|
link: user.pages.media,
|
||||||
icon: 'video_library',
|
icon: 'video_library',
|
||||||
text: 'My media',
|
text: i18next.t('My media'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +37,7 @@ function popupTopNavItems() {
|
||||||
items.push({
|
items.push({
|
||||||
link: links.signout,
|
link: links.signout,
|
||||||
icon: 'exit_to_app',
|
icon: 'exit_to_app',
|
||||||
text: 'Sign out',
|
text: i18next.t('Sign out'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ function popupMiddleNavItems() {
|
||||||
itemType: 'open-subpage',
|
itemType: 'open-subpage',
|
||||||
icon: 'brightness_4',
|
icon: 'brightness_4',
|
||||||
iconPos: 'left',
|
iconPos: 'left',
|
||||||
text: 'Switch theme',
|
text: i18next.t('Switch theme'),
|
||||||
buttonAttr: {
|
buttonAttr: {
|
||||||
className: 'change-page',
|
className: 'change-page',
|
||||||
'data-page-id': 'switch-theme',
|
'data-page-id': 'switch-theme',
|
||||||
|
@ -58,6 +60,19 @@ function popupMiddleNavItems() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasTranslations) {
|
||||||
|
items.push({
|
||||||
|
itemType: 'open-subpage',
|
||||||
|
icon: 'language',
|
||||||
|
iconPos: 'left',
|
||||||
|
text: i18next.t('Language'),
|
||||||
|
buttonAttr: {
|
||||||
|
className: 'change-page',
|
||||||
|
'data-page-id': 'language',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (user.is.anonymous) {
|
if (user.is.anonymous) {
|
||||||
if (user.can.login) {
|
if (user.can.login) {
|
||||||
items.push({
|
items.push({
|
||||||
|
@ -77,7 +92,7 @@ function popupMiddleNavItems() {
|
||||||
itemType: 'link',
|
itemType: 'link',
|
||||||
icon: 'person_add',
|
icon: 'person_add',
|
||||||
iconPos: 'left',
|
iconPos: 'left',
|
||||||
text: 'Register',
|
text: i18next.t('Register'),
|
||||||
link: links.register,
|
link: links.register,
|
||||||
linkAttr: {
|
linkAttr: {
|
||||||
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
|
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
|
||||||
|
@ -88,14 +103,14 @@ function popupMiddleNavItems() {
|
||||||
items.push({
|
items.push({
|
||||||
link: links.user.editProfile,
|
link: links.user.editProfile,
|
||||||
icon: 'brush',
|
icon: 'brush',
|
||||||
text: 'Edit profile',
|
text: i18next.t('Edit profile'),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user.can.changePassword) {
|
if (user.can.changePassword) {
|
||||||
items.push({
|
items.push({
|
||||||
link: links.changePassword,
|
link: links.changePassword,
|
||||||
icon: 'lock',
|
icon: 'lock',
|
||||||
text: 'Change password',
|
text: i18next.t('Change password'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +125,7 @@ function popupBottomNavItems() {
|
||||||
items.push({
|
items.push({
|
||||||
link: links.admin,
|
link: links.admin,
|
||||||
icon: 'admin_panel_settings',
|
icon: 'admin_panel_settings',
|
||||||
text: 'MediaCMS administration',
|
text: `MediaCMS ${i18next.t('administration')}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +134,7 @@ function popupBottomNavItems() {
|
||||||
|
|
||||||
export const HeaderContext = createContext({
|
export const HeaderContext = createContext({
|
||||||
hasThemeSwitcher,
|
hasThemeSwitcher,
|
||||||
|
hasTranslations,
|
||||||
popupNavItems: {
|
popupNavItems: {
|
||||||
top: popupTopNavItems(),
|
top: popupTopNavItems(),
|
||||||
middle: popupMiddleNavItems(),
|
middle: popupMiddleNavItems(),
|
||||||
|
@ -126,4 +142,4 @@ export const HeaderContext = createContext({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const HeaderConsumer = HeaderContext.Consumer;
|
export const HeaderConsumer = HeaderContext.Consumer;
|
||||||
|
|
37
frontend/src/static/js/utils/languages/i18n.ts
Normal file
37
frontend/src/static/js/utils/languages/i18n.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import i18n from 'i18next';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
import { enabled as langEnabled, translations as langTranslations, selected as langSelected } from '.';
|
||||||
|
|
||||||
|
// the translations
|
||||||
|
// (tip move them in a JSON file and import them,
|
||||||
|
// or even better, manage them via a UI: https://react.i18next.com/guides/multiple-translation-files#manage-your-translations-with-a-management-gui)
|
||||||
|
const resources: {
|
||||||
|
[key: string]: {
|
||||||
|
translation: { [key: string]: string };
|
||||||
|
};
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
for (let k in langEnabled) {
|
||||||
|
resources[langEnabled[k]] = { translation: langTranslations[langEnabled[k]] };
|
||||||
|
}
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(initReactI18next) // passes i18n down to react-i18next
|
||||||
|
.init({
|
||||||
|
resources,
|
||||||
|
lng: langSelected, // if you're using a language detector, do not define the lng option & // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
|
||||||
|
fallbackLng: langSelected,
|
||||||
|
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
|
||||||
|
// if you're using a language detector, do not define the lng option
|
||||||
|
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false, // react already safes from xss
|
||||||
|
},
|
||||||
|
|
||||||
|
// react-i18next options
|
||||||
|
react: {
|
||||||
|
wait: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
25
frontend/src/static/js/utils/languages/index.ts
Normal file
25
frontend/src/static/js/utils/languages/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { el, en, hi } from './translations/';
|
||||||
|
import { BrowserCache } from '../classes/';
|
||||||
|
import { config as mediacmsConfig } from '../settings/config.js';
|
||||||
|
|
||||||
|
const siteId = mediacmsConfig(window.MediaCMS).site.id;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const browserCache = new BrowserCache('MediaCMS[' + siteId + '][language]');
|
||||||
|
|
||||||
|
export const labels = {
|
||||||
|
el: 'Greek',
|
||||||
|
en: 'English',
|
||||||
|
hi: 'Hindi',
|
||||||
|
} as { [key: string]: string };
|
||||||
|
|
||||||
|
// In display order.
|
||||||
|
export const translations: { [key: string]: { [key: string]: string } } = {
|
||||||
|
en,
|
||||||
|
hi,
|
||||||
|
el,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const enabled: string[] = Object.keys(translations);
|
||||||
|
|
||||||
|
export const selected = browserCache.get('code') || 'en';
|
21
frontend/src/static/js/utils/languages/translations/el.ts
Normal file
21
frontend/src/static/js/utils/languages/translations/el.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export default {
|
||||||
|
administration: 'διαχείριση',
|
||||||
|
Categories: 'Κατηγορίες',
|
||||||
|
'Change password': 'Αλλαγή κωδικού',
|
||||||
|
'Created playlists': 'Λίστες αναπαραγωγής',
|
||||||
|
'Edit profile': 'Επεξεργασία προφίλ',
|
||||||
|
History: 'Ιστορικό',
|
||||||
|
Home: 'Αρχική',
|
||||||
|
Language: 'Γλώσσα',
|
||||||
|
Members: 'Μέλη',
|
||||||
|
'My media': 'Oι μεταφορτώσεις μου',
|
||||||
|
Register: 'Εγγραφή',
|
||||||
|
REGISTER: 'ΕΓΓΡΑΦΗ',
|
||||||
|
'Sign in': 'Σύνδεση',
|
||||||
|
'SIGN IN': 'ΣΥΝΔΕΣΗ',
|
||||||
|
'Sign out': 'Αποσύνδεση',
|
||||||
|
'SIGN OUT': 'ΑΠΟΣΥΝΔΕΣΗ',
|
||||||
|
'Switch theme': 'Επιλογή θέματος',
|
||||||
|
'Upload media': 'Προσθήκη αρχείου',
|
||||||
|
'VIEW ALL': 'ΠΡΟΒΟΛΗ ΟΛΩΝ',
|
||||||
|
} as { [key: string]: string };
|
21
frontend/src/static/js/utils/languages/translations/en.ts
Normal file
21
frontend/src/static/js/utils/languages/translations/en.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export default {
|
||||||
|
administration: 'administration',
|
||||||
|
Categories: 'Categories',
|
||||||
|
'Change password': 'Change password',
|
||||||
|
'Created playlists': 'Created playlists',
|
||||||
|
'Edit profile': 'Edit profile',
|
||||||
|
History: 'History',
|
||||||
|
Home: 'Home',
|
||||||
|
Language: 'Language',
|
||||||
|
Members: 'Members',
|
||||||
|
'My media': 'My media',
|
||||||
|
Register: 'Register',
|
||||||
|
REGISTER: 'REGISTER',
|
||||||
|
'Sign in': 'Sign in',
|
||||||
|
'SIGN IN': 'SIGN IN',
|
||||||
|
'Sign out': 'Sign out',
|
||||||
|
'SIGN OUT': 'SIGN OUT',
|
||||||
|
'Switch theme': 'Switch theme',
|
||||||
|
'Upload media': 'Upload media',
|
||||||
|
'VIEW ALL': 'VIEW ALL',
|
||||||
|
} as { [key: string]: string };
|
21
frontend/src/static/js/utils/languages/translations/hi.ts
Normal file
21
frontend/src/static/js/utils/languages/translations/hi.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export default {
|
||||||
|
administration: 'administration',
|
||||||
|
Categories: 'Categories',
|
||||||
|
'Change password': 'Change password',
|
||||||
|
'Created playlists': 'Created playlists',
|
||||||
|
'Edit profile': 'Edit profile',
|
||||||
|
History: 'History',
|
||||||
|
Home: 'Home',
|
||||||
|
Language: 'Language',
|
||||||
|
Members: 'Members',
|
||||||
|
'My media': 'My media',
|
||||||
|
Register: 'Register',
|
||||||
|
REGISTER: 'REGISTER',
|
||||||
|
'Sign in': 'Sign in',
|
||||||
|
'SIGN IN': 'SIGN IN',
|
||||||
|
'Sign out': 'Sign out',
|
||||||
|
'SIGN OUT': 'SIGN OUT',
|
||||||
|
'Switch theme': 'Switch theme',
|
||||||
|
'Upload media': 'Upload media',
|
||||||
|
'VIEW ALL': 'VIEW ALL',
|
||||||
|
} as { [key: string]: string };
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as el } from './el';
|
||||||
|
export { default as en } from './en';
|
||||||
|
export { default as hi } from './hi';
|
|
@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
|
||||||
import { ThemeProvider } from './contexts/ThemeContext';
|
import { ThemeProvider } from './contexts/ThemeContext';
|
||||||
import { LayoutProvider } from './contexts/LayoutContext';
|
import { LayoutProvider } from './contexts/LayoutContext';
|
||||||
import { UserProvider } from './contexts/UserContext';
|
import { UserProvider } from './contexts/UserContext';
|
||||||
|
import './languages/i18n';
|
||||||
|
|
||||||
const AppProviders = ({ children }) => (
|
const AppProviders = ({ children }) => (
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
|
|
Loading…
Reference in a new issue