Compare commits

...

4 commits

Author SHA1 Message Date
Markos Gogoulos 39fe66641a Merge branch 'main' into frontend-i18n 2021-08-01 20:15:06 +03:00
styiannis 512cd33e3a added sidebar's home page link translation 2021-07-17 20:55:53 +03:00
styiannis 656128a86a added languages menu options 2021-07-17 20:46:03 +03:00
styiannis 687a0016e9 initial translations related commit 2021-07-17 20:42:45 +03:00
19 changed files with 453 additions and 58 deletions

View file

@ -10,10 +10,12 @@
"dependencies": {
"axios": "^0.21.1",
"flux": "^4.0.1",
"i18next": "^20.3.3",
"mediacms-player": "file:packages/player",
"normalize.css": "^8.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-i18next": "^11.11.3",
"sortablejs": "^1.13.0",
"timeago.js": "^4.0.2",
"url-parse": "^1.5.1"
@ -8619,6 +8621,14 @@
"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": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/html-prettify/-/html-prettify-1.0.3.tgz",
@ -8752,6 +8762,14 @@
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"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": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -13205,6 +13223,19 @@
"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": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -20404,6 +20435,14 @@
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"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": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
@ -21821,6 +21860,7 @@
}
},
"packages/vjs-plugin": {
"name": "mediacms-vjs-plugin",
"version": "0.9.0",
"license": "Apache-2.0",
"dependencies": {
@ -21854,6 +21894,7 @@
}
},
"packages/vjs-plugin-font-icons": {
"name": "mediacms-vjs-plugin-font-icons",
"version": "0.9.0",
"license": "Apache-2.0",
"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": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/html-prettify/-/html-prettify-1.0.3.tgz",
@ -28798,6 +28847,14 @@
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"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": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -32276,6 +32333,15 @@
"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": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -38069,6 +38135,11 @@
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"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": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",

View file

@ -42,10 +42,12 @@
"dependencies": {
"axios": "^0.21.1",
"flux": "^4.0.1",
"i18next": "^20.3.3",
"mediacms-player": "file:packages/player",
"normalize.css": "^8.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-i18next": "^11.11.3",
"sortablejs": "^1.13.0",
"timeago.js": "^4.0.2",
"url-parse": "^1.5.1"

View file

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
interface MediaListHeaderProps {
title?: string;
@ -9,7 +10,8 @@ interface MediaListHeaderProps {
}
export const MediaListHeader: React.FC<MediaListHeaderProps> = (props) => {
const viewAllText = props.viewAllText || 'VIEW ALL';
const { t } = useTranslation();
const viewAllText = props.viewAllText || t('VIEW ALL');
return (
<div className={(props.className ? props.className + ' ' : '') + 'media-list-header'} style={props.style}>
<h2>{props.title}</h2>

View file

@ -1,11 +1,49 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useLayout, usePopup } from '../../../utils/hooks/';
import { PageStore } from '../../../utils/stores/';
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 { 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 = {
main: null,
};
@ -56,14 +94,7 @@ function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher) {
pages['switch-theme'] = (
<div>
<PopupTop>
<div>
<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>
<OpenThemeSwitcher />
</PopupTop>
<PopupMain>
<HeaderThemeSwitcher />
@ -72,21 +103,37 @@ function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher) {
);
}
if (hasTranslations) {
pages['language'] = (
<div>
<PopupTop>
<OpenLanguageOptions />
</PopupTop>
<PopupMain>
<LanguageOptions />
</PopupMain>
</div>
);
}
return pages;
}
function UploadMediaButton({ user, links }) {
const { t } = useTranslation();
return !user.is.anonymous && user.can.addMedia ? (
<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" />
<span className="hidden-txt">Upload media</span>
<span className="hidden-txt">{t('Upload media')}</span>
</CircleIconButton>
</div>
) : null;
}
function LoginButton({ user, link, hasHeaderThemeSwitcher }) {
const { t } = useTranslation();
return user.is.anonymous && user.can.login ? (
<div className="sign-in-wrap">
<a
@ -95,15 +142,17 @@ function LoginButton({ user, link, hasHeaderThemeSwitcher }) {
className={
'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>
</div>
) : null;
}
function RegisterButton({ user, link, hasHeaderThemeSwitcher }) {
const { t } = useTranslation();
return user.is.anonymous && user.can.register ? (
<div className="register-wrap">
<a
@ -112,9 +161,9 @@ function RegisterButton({ user, link, hasHeaderThemeSwitcher }) {
'button-link register-link' +
(hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
}
title="Register"
title="{t('REGISTER')}"
>
Register
{t('REGISTER')}
</a>
</div>
) : null;
@ -144,7 +193,9 @@ export function HeaderRight(props) {
<div
className={
(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}>
@ -160,7 +211,12 @@ export function HeaderRight(props) {
<PopupContent contentRef={popupContentRef}>
<NavigationContentApp
initPage="main"
pages={headerPopupPages(user, header.popupNavItems, header.hasThemeSwitcher)}
pages={headerPopupPages(
user,
header.popupNavItems,
header.hasThemeSwitcher,
header.hasTranslations
)}
pageChangeSelector={'.change-page'}
pageIdSelectorAttr={'data-page-id'}
/>

View file

@ -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;
}
}
}

View file

@ -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>
);
};

View file

@ -196,7 +196,6 @@ $__button-link-horizontal-padding: 16;
font-weight: 500;
line-height: 1;
display: block;
text-transform: uppercase;
}
.signin-icon-link {

View file

@ -1,4 +1,5 @@
import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import urlParse from 'url-parse';
import { useUser } from '../../../utils/hooks/';
import { PageStore } from '../../../utils/stores/';
@ -34,13 +35,15 @@ export function SidebarNavigationMenu() {
}
function MainMenuFirstSection() {
const { t } = useTranslation();
const items = [];
if (!sidebar.hideHomeLink) {
items.push({
link: links.home,
icon: 'home',
text: 'Home',
text: t('Home'),
className: 'nav-item-home',
});
}

View file

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ApiUrlConsumer } from '../utils/contexts/';
import { MediaListWrapper } from '../components/MediaListWrapper';
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
@ -9,18 +10,21 @@ interface CategoriesPageProps {
title?: string;
}
export const CategoriesPage: React.FC<CategoriesPageProps> = ({ id = 'categories', title = 'Categories' }) => (
<Page id={id}>
<ApiUrlConsumer>
{(apiUrl) => (
<MediaListWrapper title={title} className="items-list-ver">
<LazyLoadItemListAsync
singleLinkContent={true}
inCategoriesList={true}
requestUrl={apiUrl.archive.categories}
/>
</MediaListWrapper>
)}
</ApiUrlConsumer>
</Page>
);
export const CategoriesPage: React.FC<CategoriesPageProps> = ({ id = 'categories', title }) => {
const { t } = useTranslation();
return (
<Page id={id}>
<ApiUrlConsumer>
{(apiUrl) => (
<MediaListWrapper title={title || t('Categories')} className="items-list-ver">
<LazyLoadItemListAsync
singleLinkContent={true}
inCategoriesList={true}
requestUrl={apiUrl.archive.categories}
/>
</MediaListWrapper>
)}
</ApiUrlConsumer>
</Page>
);
};

View file

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ApiUrlConsumer } from '../utils/contexts/';
import { MediaListWrapper } from '../components/MediaListWrapper';
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
@ -9,14 +10,17 @@ interface MembersPageProps {
title?: string;
}
export const MembersPage: React.FC<MembersPageProps> = ({ id = 'members', title = 'Members' }) => (
<Page id={id}>
<ApiUrlConsumer>
{(apiUrl) => (
<MediaListWrapper title={title} className="items-list-ver">
<LazyLoadItemListAsync requestUrl={apiUrl.users} />
</MediaListWrapper>
)}
</ApiUrlConsumer>
</Page>
);
export const MembersPage: React.FC<MembersPageProps> = ({ id = 'members', title }) => {
const { t } = useTranslation();
return (
<Page id={id}>
<ApiUrlConsumer>
{(apiUrl) => (
<MediaListWrapper title={title || t('Members')} className="items-list-ver">
<LazyLoadItemListAsync requestUrl={apiUrl.users} />
</MediaListWrapper>
)}
</ApiUrlConsumer>
</Page>
);
};

View file

@ -1,4 +1,5 @@
import React from 'react';
import { withTranslation } from 'react-i18next';
import { ApiUrlConsumer } from '../utils/contexts/';
import { PageStore } from '../utils/stores/';
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 { ProfileMediaPage } from './ProfileMediaPage';
export class ProfilePlaylistsPage extends ProfileMediaPage {
class ProfilePlaylistsPageClass extends ProfileMediaPage {
constructor(props) {
super(props, 'author-playlists');
@ -37,7 +38,7 @@ export class ProfilePlaylistsPage extends ProfileMediaPage {
<ApiUrlConsumer>
{(apiUrl) => (
<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"
>
<LazyLoadItemListAsync
@ -55,3 +56,7 @@ export class ProfilePlaylistsPage extends ProfileMediaPage {
];
}
}
const ProfilePlaylistsPage = withTranslation()(ProfilePlaylistsPageClass);
export { ProfilePlaylistsPage };

View file

@ -1,4 +1,5 @@
import React, { createContext } from 'react';
import i18next from 'i18next';
import { config as mediacmsConfig } from '../settings/config.js';
const config = mediacmsConfig(window.MediaCMS);
@ -8,6 +9,7 @@ const theme = config.theme;
const user = config.member;
const hasThemeSwitcher = theme.switch.enabled && 'header' === theme.switch.position;
const hasTranslations = true;
function popupTopNavItems() {
const items = [];
@ -17,7 +19,7 @@ function popupTopNavItems() {
items.push({
link: links.user.addMedia,
icon: 'video_call',
text: 'Upload media',
text: i18next.t('Upload media'),
itemAttr: {
className: 'visible-only-in-small',
},
@ -27,7 +29,7 @@ function popupTopNavItems() {
items.push({
link: user.pages.media,
icon: 'video_library',
text: 'My media',
text: i18next.t('My media'),
});
}
}
@ -35,7 +37,7 @@ function popupTopNavItems() {
items.push({
link: links.signout,
icon: 'exit_to_app',
text: 'Sign out',
text: i18next.t('Sign out'),
});
}
@ -50,7 +52,7 @@ function popupMiddleNavItems() {
itemType: 'open-subpage',
icon: 'brightness_4',
iconPos: 'left',
text: 'Switch theme',
text: i18next.t('Switch theme'),
buttonAttr: {
className: 'change-page',
'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.can.login) {
items.push({
@ -77,7 +92,7 @@ function popupMiddleNavItems() {
itemType: 'link',
icon: 'person_add',
iconPos: 'left',
text: 'Register',
text: i18next.t('Register'),
link: links.register,
linkAttr: {
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
@ -88,14 +103,14 @@ function popupMiddleNavItems() {
items.push({
link: links.user.editProfile,
icon: 'brush',
text: 'Edit profile',
text: i18next.t('Edit profile'),
});
if (user.can.changePassword) {
items.push({
link: links.changePassword,
icon: 'lock',
text: 'Change password',
text: i18next.t('Change password'),
});
}
}
@ -110,7 +125,7 @@ function popupBottomNavItems() {
items.push({
link: links.admin,
icon: 'admin_panel_settings',
text: 'MediaCMS administration',
text: `MediaCMS ${i18next.t('administration')}`,
});
}
@ -119,6 +134,7 @@ function popupBottomNavItems() {
export const HeaderContext = createContext({
hasThemeSwitcher,
hasTranslations,
popupNavItems: {
top: popupTopNavItems(),
middle: popupMiddleNavItems(),
@ -126,4 +142,4 @@ export const HeaderContext = createContext({
},
});
export const HeaderConsumer = HeaderContext.Consumer;
export const HeaderConsumer = HeaderContext.Consumer;

View 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;

View 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';

View 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 };

View 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 };

View 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 };

View file

@ -0,0 +1,3 @@
export { default as el } from './el';
export { default as en } from './en';
export { default as hi } from './hi';

View file

@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
import { ThemeProvider } from './contexts/ThemeContext';
import { LayoutProvider } from './contexts/LayoutContext';
import { UserProvider } from './contexts/UserContext';
import './languages/i18n';
const AppProviders = ({ children }) => (
<LayoutProvider>