Compare commits
2 commits
main
...
feat-trans
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c2b7c2d2fd | ||
![]() |
3d0370da34 |
51 changed files with 512 additions and 69 deletions
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
|
||||
from celery.schedules import crontab
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
@ -304,6 +305,7 @@ INSTALLED_APPS = [
|
|||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
|
@ -487,3 +489,21 @@ if GLOBAL_LOGIN_REQUIRED:
|
|||
]
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
try:
|
||||
# mostly used in docker-compose-dev.yaml
|
||||
DEVELOPMENT_MODE = os.environ.get("DEVELOPMENT_MODE")
|
||||
if DEVELOPMENT_MODE and DEVELOPMENT_MODE == 'True':
|
||||
# keep a dev_settings.py file for local overrides
|
||||
from .dev_settings import * # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
LANGUAGES = [
|
||||
('el', _('Greek')),
|
||||
("de", _("German")),
|
||||
("en", _("English")),
|
||||
('fr', _('French')),
|
||||
]
|
||||
|
||||
LANGUAGE_CODE = 'en' # default language
|
||||
|
|
|
@ -13,6 +13,7 @@ schema_view = get_schema_view(
|
|||
permission_classes=(AllowAny,),
|
||||
)
|
||||
|
||||
# refactor seriously
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r"^__debug__/", include(debug_toolbar.urls)),
|
||||
|
|
|
@ -28,7 +28,7 @@ else
|
|||
fi
|
||||
|
||||
# We should do this only for folders that have a different owner, since it is an expensive operation
|
||||
find /home/mediacms.io/ ! \( -user www-data -group $TARGET_GID \) -exec chown www-data:$TARGET_GID {} +
|
||||
# find /home/mediacms.io/ ! \( -user www-data -group $TARGET_GID \) -exec chown www-data:$TARGET_GID {} +
|
||||
|
||||
chmod +x /home/mediacms.io/mediacms/deploy/docker/start.sh /home/mediacms.io/mediacms/deploy/docker/prestart.sh
|
||||
|
||||
|
|
|
@ -1,6 +1,35 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
migrations:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile-dev
|
||||
image: mediacms/mediacms-dev:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
ADMIN_PASSWORD: 'admin'
|
||||
FRONTEND_HOST: 'http://localhost'
|
||||
PORTAL_NAME: 'MediaCMS'
|
||||
SECRET_KEY: 'ma!s3^b-cw!f#7s6s0m3*jx77a@riw(7701**(r=ww%w!2+yk2'
|
||||
POSTGRES_HOST: 'db'
|
||||
DEVELOPMENT_MODE: "True"
|
||||
REDIS_SSL_CERT_REQS: "none"
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
frontend:
|
||||
image: node:14
|
||||
volumes:
|
||||
|
@ -18,19 +47,23 @@ services:
|
|||
context: .
|
||||
dockerfile: ./Dockerfile-dev
|
||||
image: mediacms/mediacms-dev:latest
|
||||
command: "python manage.py runserver 0.0.0.0:80"
|
||||
environment:
|
||||
DEVELOPMENT_MODE: "True"
|
||||
REDIS_SSL_CERT_REQS: "none"
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_PASSWORD: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
FRONTEND_HOST: 'http://localhost'
|
||||
PORTAL_NAME: 'MediaCMS'
|
||||
SECRET_KEY: 'ma!s3^b-cw!f#7s6s0m3*jx77a@riw(7701**(r=ww%w!2+yk2'
|
||||
POSTGRES_HOST: 'db'
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:15.2-alpine
|
||||
volumes:
|
||||
|
@ -54,3 +87,22 @@ services:
|
|||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
celery_worker:
|
||||
image: mediacms/mediacms-dev:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
FRONTEND_HOST: 'http://localhost'
|
||||
PORTAL_NAME: 'MediaCMS'
|
||||
SECRET_KEY: 'ma!s3^b-cw!f#7s6s0m3*jx77a@riw(7701**(r=ww%w!2+yk2'
|
||||
POSTGRES_HOST: 'db'
|
||||
REDIS_SSL_CERT_REQS: "none"
|
||||
DEVELOPMENT_MODE: "True"
|
||||
depends_on:
|
||||
- web
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.conf import settings
|
||||
|
||||
from .frontend_translations import get_frontend_translations
|
||||
from .methods import is_mediacms_editor, is_mediacms_manager
|
||||
|
||||
|
||||
|
@ -31,4 +32,6 @@ def stuff(request):
|
|||
ret["ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY
|
||||
ret["VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE
|
||||
ret["RSS_URL"] = "/rss"
|
||||
ret["FRONTEND_TRANSLATIONS"] = get_frontend_translations(request.LANGUAGE_CODE)
|
||||
|
||||
return ret
|
||||
|
|
28
files/frontend_translations/__init__.py
Normal file
28
files/frontend_translations/__init__.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from django.conf import settings
|
||||
|
||||
from .de import translations as de_translations
|
||||
from .el import translations as el_translations
|
||||
from .fr import translations as fr_translations
|
||||
|
||||
translations = {}
|
||||
translations['el'] = el_translations
|
||||
translations['fr'] = fr_translations
|
||||
translations['de'] = de_translations
|
||||
|
||||
|
||||
def get_frontend_translations(language_code):
|
||||
if language_code not in [pair[0] for pair in settings.LANGUAGES]:
|
||||
return {}
|
||||
|
||||
if language_code in ['en', 'en-us', 'en-gb']:
|
||||
return {}
|
||||
|
||||
translation = translations[language_code]
|
||||
|
||||
# replace any keys from translation that contains a space with an underscore
|
||||
# do not keep initial keys that contain a space
|
||||
for key in list(translation.keys()):
|
||||
if ' ' in key:
|
||||
translation[key.replace(' ', '_')] = translation.pop(key)
|
||||
|
||||
return translation
|
37
files/frontend_translations/de.py
Normal file
37
files/frontend_translations/de.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# German translation for strings in frontend app
|
||||
|
||||
translations = {
|
||||
"Home": "Startseite",
|
||||
"Featured": "Empfohlen",
|
||||
"Latest": "Neueste",
|
||||
"Recommended": "Empfehlungen",
|
||||
"Recent uploads": "Kürzlich hochgeladen",
|
||||
"Tags": "Tags",
|
||||
"Categories": "Kategorien",
|
||||
"Members": "Mitglieder",
|
||||
"Upload": "Hochladen",
|
||||
"Upload media": "Medien hochladen",
|
||||
"My media": "Meine Medien",
|
||||
"My playlists": "Meine Wiedergabelisten",
|
||||
"History": "Verlauf",
|
||||
"Liked media": "Gefällt mir Medien",
|
||||
"About": "Über",
|
||||
"Terms": "Nutzungsbedingungen",
|
||||
"Contact": "Kontakt",
|
||||
"Language": "Sprache",
|
||||
"Manage media": "Medien verwalten",
|
||||
"Manage users": "Benutzer verwalten",
|
||||
"Manage comments": "Kommentare verwalten",
|
||||
"View all": "Alle anzeigen",
|
||||
"VIEW ALL": "ALLE ANZEIGEN",
|
||||
"view": "Ansicht",
|
||||
"views": "Ansichten",
|
||||
"Search": "Suchen",
|
||||
"Up next": "Als Nächstes",
|
||||
"AUTOPLAY": "AUTOPLAY",
|
||||
"Sign out": "Abmelden",
|
||||
"Sign in": "Anmelden",
|
||||
"Register": "Registrieren",
|
||||
"Edit profile": "Profil bearbeiten",
|
||||
"Change password": "Passwort ändern",
|
||||
}
|
37
files/frontend_translations/el.py
Normal file
37
files/frontend_translations/el.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Greek translation for strings in frontend app
|
||||
|
||||
translations = {
|
||||
"Home": "Αρχική",
|
||||
"Featured": "Επιλεγμένα",
|
||||
"Latest": "Πρόσφατα",
|
||||
"Recommended": "Προτεινόμενα",
|
||||
"Recent uploads": "Πρόσφατα αρχεία",
|
||||
"Tags": "Ετικέτες",
|
||||
"Categories": "Κατηγορίες",
|
||||
"Members": "Μέλη",
|
||||
"Upload": "Ανέβασμα αρχείου",
|
||||
"Upload media": "Ανέβασμα αρχείων",
|
||||
"My media": "Τα αρχεία μου",
|
||||
"My playlists": "Οι λίστες μου",
|
||||
"History": "Ιστορικό",
|
||||
"Liked media": "Αγαπημένα",
|
||||
"About": "Σχετικά",
|
||||
"Terms": "Όροι",
|
||||
"Contact": "Επικοινωνία",
|
||||
"Language": "Γλώσσα",
|
||||
"Manage media": "Διαχείριση αρχείων",
|
||||
"Manage users": "Διαχείριση χρηστών",
|
||||
"Manage comments": "Διαχείριση σχολίων",
|
||||
"View all": "Δές τα όλα",
|
||||
"VIEW ALL": "ΔΕΣ ΤΑ ΟΛΑ",
|
||||
"view": "προβολή",
|
||||
"views": "προβολές",
|
||||
"Search": "Αναζήτηση",
|
||||
"Up next": "Επόμενο",
|
||||
"AUTOPLAY": "Αυτόματη αναπαραγωγή",
|
||||
"Sign out": "Αποσύνδεση",
|
||||
"Sign in": "Σύνδεση",
|
||||
"Register": "Εγγραφή",
|
||||
"Edit profile": "Επεξεργασία προφιλ",
|
||||
"Change password": "Αλλαγή κωδικού",
|
||||
}
|
37
files/frontend_translations/en.py
Normal file
37
files/frontend_translations/en.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Translations listing
|
||||
|
||||
translations = {
|
||||
"Home": "",
|
||||
"Featured": "",
|
||||
"Latest": "",
|
||||
"Recommended": "",
|
||||
"Recent uploads": "",
|
||||
"Tags": "",
|
||||
"Categories": "",
|
||||
"Members": "",
|
||||
"Upload": "",
|
||||
"Upload media": "",
|
||||
"My media": "",
|
||||
"My playlists": "",
|
||||
"History": "",
|
||||
"Liked media": "",
|
||||
"About": "",
|
||||
"Terms": "",
|
||||
"Contact": "",
|
||||
"Language": "",
|
||||
"Manage media": "",
|
||||
"Manage users": "",
|
||||
"Manage comments": "",
|
||||
"View all": "",
|
||||
"VIEW ALL": "",
|
||||
"view": "",
|
||||
"views": "",
|
||||
"Search": "",
|
||||
"Up next": "",
|
||||
"AUTOPLAY": "",
|
||||
"Sign out": "",
|
||||
"Sign in": "",
|
||||
"Register": "",
|
||||
"Edit profile": "",
|
||||
"Change password": "",
|
||||
}
|
37
files/frontend_translations/fr.py
Normal file
37
files/frontend_translations/fr.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# French translation for strings in frontend app
|
||||
|
||||
translations = {
|
||||
"Home": "Accueil",
|
||||
"Featured": "En vedette",
|
||||
"Latest": "Dernier",
|
||||
"Recommended": "Recommandé",
|
||||
"Recent uploads": "Téléchargements récents",
|
||||
"Tags": "Tags",
|
||||
"Categories": "Catégories",
|
||||
"Members": "Membres",
|
||||
"Upload": "Télécharger",
|
||||
"Upload media": "Télécharger un média",
|
||||
"My media": "Mes médias",
|
||||
"My playlists": "Mes listes de lecture",
|
||||
"History": "Historique",
|
||||
"Liked media": "Médias aimés",
|
||||
"About": "À propos",
|
||||
"Terms": "Conditions",
|
||||
"Contact": "Contact",
|
||||
"Language": "Langue",
|
||||
"Manage media": "Gérer les médias",
|
||||
"Manage users": "Gérer les utilisateurs",
|
||||
"Manage comments": "Gérer les commentaires",
|
||||
"View all": "Voir tout",
|
||||
"VIEW ALL": "VOIR TOUT",
|
||||
"view": "vue",
|
||||
"views": "vues",
|
||||
"Search": "Recherche",
|
||||
"Up next": "À suivre",
|
||||
"AUTOPLAY": "LECTURE AUTOMATIQUE",
|
||||
"Sign out": "Se déconnecter",
|
||||
"Sign in": "Se connecter",
|
||||
"Register": "S'inscrire",
|
||||
"Edit profile": "Modifier le profil",
|
||||
"Change password": "Changer le mot de passe",
|
||||
}
|
|
@ -7,8 +7,10 @@ from . import management_views, views
|
|||
from .feeds import IndexRSSFeed, SearchRSSFeed
|
||||
|
||||
urlpatterns = [
|
||||
path("i18n/", include("django.conf.urls.i18n")),
|
||||
re_path(r"^$", views.index),
|
||||
re_path(r"^about", views.about, name="about"),
|
||||
re_path(r"^setlanguage", views.setlanguage, name="setlanguage"),
|
||||
re_path(r"^add_subtitle", views.add_subtitle, name="add_subtitle"),
|
||||
re_path(r"^categories$", views.categories, name="categories"),
|
||||
re_path(r"^contact$", views.contact, name="contact"),
|
||||
|
|
|
@ -74,6 +74,13 @@ def about(request):
|
|||
return render(request, "cms/about.html", context)
|
||||
|
||||
|
||||
def setlanguage(request):
|
||||
"""Set Language view"""
|
||||
|
||||
context = {}
|
||||
return render(request, "cms/set_language.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def add_subtitle(request):
|
||||
"""Add subtitle view"""
|
||||
|
@ -287,7 +294,7 @@ def search(request):
|
|||
"""Search view"""
|
||||
|
||||
context = {}
|
||||
RSS_URL = f"/rss{request.environ['REQUEST_URI']}"
|
||||
RSS_URL = f"/rss{request.environ.get('REQUEST_URI')}"
|
||||
context["RSS_URL"] = RSS_URL
|
||||
return render(request, "cms/search.html", context)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
interface MediaListHeaderProps {
|
||||
title?: string;
|
||||
|
@ -9,7 +10,7 @@ interface MediaListHeaderProps {
|
|||
}
|
||||
|
||||
export const MediaListHeader: React.FC<MediaListHeaderProps> = (props) => {
|
||||
const viewAllText = props.viewAllText || 'VIEW ALL';
|
||||
const viewAllText = props.viewAllText || translate_string('VIEW ALL');
|
||||
return (
|
||||
<div className={(props.className ? props.className + ' ' : '') + 'media-list-header'} style={props.style}>
|
||||
<h2>{props.title}</h2>
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { format } from 'timeago.js';
|
||||
import { formatViewsNumber, imageExtension } from '../../../../utils/helpers/';
|
||||
import { VideoPlayerByPageLink } from '../../../video-player/VideoPlayerByPageLink';
|
||||
import { translate_string } from '../../../../utils/helpers/';
|
||||
|
||||
export function ItemDescription(props) {
|
||||
return '' === props.description ? null : (
|
||||
|
@ -135,7 +136,7 @@ export function MediaItemAuthorLink(props) {
|
|||
|
||||
export function MediaItemMetaViews(props) {
|
||||
return (
|
||||
<span className="item-views">{formatViewsNumber(props.views) + ' ' + (1 >= props.views ? 'view' : 'views')}</span>
|
||||
<span className="item-views">{formatViewsNumber(props.views) + ' ' + (1 >= props.views ? translate_string('view') : translate_string('views'))}</span>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||
import { PageActions } from '../../utils/actions/';
|
||||
import { PageStore, MediaPageStore } from '../../utils/stores/';
|
||||
import { ItemList } from '../item-list/ItemList';
|
||||
import { translate_string } from '../../utils/helpers/';
|
||||
|
||||
function autoPlayMedia() {
|
||||
const dt = MediaPageStore.get('media-data');
|
||||
|
@ -38,10 +39,10 @@ export function AutoPlay(props) {
|
|||
return !media ? null : (
|
||||
<div className="auto-play">
|
||||
<div className="auto-play-header">
|
||||
<div className="next-label">Up next</div>
|
||||
<div className="next-label">{translate_string("Up next")}</div>
|
||||
<div className="auto-play-option">
|
||||
<label className="checkbox-label right-selectbox" tabIndex={0} onKeyPress={onKeyPress}>
|
||||
AUTOPLAY
|
||||
{translate_string("AUTOPLAY")}
|
||||
<span className="checkbox-switcher-wrap">
|
||||
<span className="checkbox-switcher">
|
||||
<input
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PageStore } from '../../../utils/stores/';
|
|||
import { HeaderConsumer, MemberConsumer, LinksConsumer } from '../../../utils/contexts/';
|
||||
import { CircleIconButton, MaterialIcon, NavigationContentApp, NavigationMenuList, PopupTop, PopupMain, UserThumbnail } from '../../_shared';
|
||||
import { HeaderThemeSwitcher } from './HeaderThemeSwitcher';
|
||||
import { translate_string } from '../../../utils/helpers/';
|
||||
|
||||
function headerPopupPages(user, popupNavItems, hasHeaderThemeSwitcher) {
|
||||
const pages = {
|
||||
|
@ -95,9 +96,9 @@ function LoginButton({ user, link, hasHeaderThemeSwitcher }) {
|
|||
className={
|
||||
'button-link sign-in' + (hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
|
||||
}
|
||||
title="Sign in"
|
||||
title={translate_string('Sign in')}
|
||||
>
|
||||
Sign in
|
||||
{translate_string('Sign in')}
|
||||
</a>
|
||||
</div>
|
||||
) : null;
|
||||
|
@ -112,9 +113,9 @@ function RegisterButton({ user, link, hasHeaderThemeSwitcher }) {
|
|||
'button-link register-link' +
|
||||
(hasHeaderThemeSwitcher ? ' hidden-only-in-small' : ' hidden-only-in-extra-small')
|
||||
}
|
||||
title="Register"
|
||||
title={translate_string('Register')}
|
||||
>
|
||||
Register
|
||||
{translate_string('Register')}
|
||||
</a>
|
||||
</div>
|
||||
) : null;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { LinksContext } from '../../../utils/contexts/';
|
|||
import { PageStore, SearchFieldStore } from '../../../utils/stores/';
|
||||
import { SearchFieldActions } from '../../../utils/actions/';
|
||||
import { MaterialIcon, PopupMain } from '../../_shared';
|
||||
import { translate_string } from '../../../utils/helpers/';
|
||||
|
||||
import './SearchField.scss';
|
||||
|
||||
|
@ -296,7 +297,7 @@ export function SearchField(props) {
|
|||
<input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
placeholder={translate_string("Search")}
|
||||
aria-label="Search"
|
||||
name="q"
|
||||
value={queryVal}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useUser } from '../../../utils/hooks/';
|
|||
import { PageStore } from '../../../utils/stores/';
|
||||
import { LinksContext, SidebarContext } from '../../../utils/contexts/';
|
||||
import { NavigationMenuList } from '../../_shared';
|
||||
import { translate_string } from '../../../utils/helpers/';
|
||||
|
||||
export function SidebarNavigationMenu() {
|
||||
const { userCan, isAnonymous, pages: userPages } = useUser();
|
||||
|
@ -40,7 +41,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.home,
|
||||
icon: 'home',
|
||||
text: 'Home',
|
||||
text: translate_string('Home'),
|
||||
className: 'nav-item-home',
|
||||
});
|
||||
}
|
||||
|
@ -49,7 +50,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.featured,
|
||||
icon: 'star',
|
||||
text: PageStore.get('config-enabled').pages.featured.title,
|
||||
text: translate_string('Featured'),
|
||||
className: 'nav-item-featured',
|
||||
});
|
||||
}
|
||||
|
@ -61,7 +62,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.recommended,
|
||||
icon: 'done_outline',
|
||||
text: PageStore.get('config-enabled').pages.recommended.title,
|
||||
text: translate_string("Recommended"),
|
||||
className: 'nav-item-recommended',
|
||||
});
|
||||
}
|
||||
|
@ -70,7 +71,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.latest,
|
||||
icon: 'new_releases',
|
||||
text: PageStore.get('config-enabled').pages.latest.title,
|
||||
text: translate_string("Latest"),
|
||||
className: 'nav-item-latest',
|
||||
});
|
||||
}
|
||||
|
@ -83,7 +84,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.archive.tags,
|
||||
icon: 'local_offer',
|
||||
text: PageStore.get('config-enabled').taxonomies.tags.title,
|
||||
text: translate_string("Tags"),
|
||||
className: 'nav-item-tags',
|
||||
});
|
||||
}
|
||||
|
@ -96,7 +97,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.archive.categories,
|
||||
icon: 'list_alt',
|
||||
text: PageStore.get('config-enabled').taxonomies.categories.title,
|
||||
text: translate_string("Categories"),
|
||||
className: 'nav-item-categories',
|
||||
});
|
||||
}
|
||||
|
@ -105,7 +106,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.members,
|
||||
icon: 'people',
|
||||
text: PageStore.get('config-enabled').pages.members.title,
|
||||
text: translate_string("Members"),
|
||||
className: 'nav-item-members',
|
||||
});
|
||||
}
|
||||
|
@ -132,7 +133,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.user.addMedia,
|
||||
icon: 'video_call',
|
||||
text: 'Upload media',
|
||||
text: translate_string("Upload"),
|
||||
className: 'nav-item-upload-media',
|
||||
});
|
||||
|
||||
|
@ -140,7 +141,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: userPages.media,
|
||||
icon: 'video_library',
|
||||
text: 'My media',
|
||||
text: translate_string("My media"),
|
||||
className: 'nav-item-my-media',
|
||||
});
|
||||
}
|
||||
|
@ -150,7 +151,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: userPages.playlists,
|
||||
icon: 'playlist_play',
|
||||
text: 'My playlists',
|
||||
text: translate_string("My playlists"),
|
||||
className: 'nav-item-my-playlists',
|
||||
});
|
||||
}
|
||||
|
@ -166,7 +167,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.user.history,
|
||||
icon: 'history',
|
||||
text: PageStore.get('config-enabled').pages.history.title,
|
||||
text: translate_string("History"),
|
||||
className: 'nav-item-history',
|
||||
});
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.user.liked,
|
||||
icon: 'thumb_up',
|
||||
text: PageStore.get('config-enabled').pages.liked.title,
|
||||
text: translate_string("Liked media"),
|
||||
className: 'nav-item-liked',
|
||||
});
|
||||
}
|
||||
|
@ -188,7 +189,35 @@ export function SidebarNavigationMenu() {
|
|||
}
|
||||
|
||||
function CustomMenuSection() {
|
||||
const items = PageStore.get('config-contents').sidebar.navMenu.items;
|
||||
const items = [];
|
||||
|
||||
items.push({
|
||||
link: '/about',
|
||||
icon: 'contact_support',
|
||||
text: translate_string("About"),
|
||||
className: 'nav-item-about',
|
||||
});
|
||||
|
||||
items.push({
|
||||
link: '/tos',
|
||||
icon: 'description',
|
||||
text: translate_string("Terms"),
|
||||
className: 'nav-item-terms',
|
||||
});
|
||||
|
||||
items.push({
|
||||
link: '/contact',
|
||||
icon: 'alternate_email',
|
||||
text: translate_string("Contact"),
|
||||
className: 'nav-item-contact',
|
||||
});
|
||||
|
||||
items.push({
|
||||
link: '/setlanguage',
|
||||
icon: 'language',
|
||||
text: translate_string("Language"),
|
||||
className: 'nav-item-language',
|
||||
});
|
||||
|
||||
return items.length ? <NavigationMenuList key="custom" items={formatItems(items)} /> : null;
|
||||
}
|
||||
|
@ -200,7 +229,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.manage.media,
|
||||
icon: 'miscellaneous_services',
|
||||
text: 'Manage media',
|
||||
text: translate_string("Manage media"),
|
||||
className: 'nav-item-manage-media',
|
||||
});
|
||||
}
|
||||
|
@ -209,7 +238,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.manage.users,
|
||||
icon: 'miscellaneous_services',
|
||||
text: 'Manage users',
|
||||
text: translate_string("Manage users"),
|
||||
className: 'nav-item-manage-users',
|
||||
});
|
||||
}
|
||||
|
@ -218,7 +247,7 @@ export function SidebarNavigationMenu() {
|
|||
items.push({
|
||||
link: links.manage.comments,
|
||||
icon: 'miscellaneous_services',
|
||||
text: 'Manage comments',
|
||||
text: translate_string("Manage comments"),
|
||||
className: 'nav-item-manage-comments',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,13 +3,14 @@ import { ApiUrlConsumer } from '../utils/contexts/';
|
|||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
interface CategoriesPageProps {
|
||||
id?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const CategoriesPage: React.FC<CategoriesPageProps> = ({ id = 'categories', title = 'Categories' }) => (
|
||||
export const CategoriesPage: React.FC<CategoriesPageProps> = ({ id = 'categories', title = translate_string('Categories') }) => (
|
||||
<Page id={id}>
|
||||
<ApiUrlConsumer>
|
||||
{(apiUrl) => (
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PageStore } from '../utils/stores/';
|
|||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
interface FeaturedMediaPageProps {
|
||||
id?: string;
|
||||
|
@ -12,7 +13,7 @@ interface FeaturedMediaPageProps {
|
|||
|
||||
export const FeaturedMediaPage: React.FC<FeaturedMediaPageProps> = ({
|
||||
id = 'featured-media',
|
||||
title = PageStore.get('config-enabled').pages.featured.title,
|
||||
title = translate_string('Featured'),
|
||||
}) => (
|
||||
<Page id={id}>
|
||||
<ApiUrlConsumer>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { MediaListWrapper } from '../components/MediaListWrapper';
|
|||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||
import { ProfileHistoryPage } from './ProfileHistoryPage';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -21,7 +22,7 @@ interface AnonymousHistoryPageProps {
|
|||
|
||||
export const AnonymousHistoryPage: React.FC<AnonymousHistoryPageProps> = ({
|
||||
id = 'history-media',
|
||||
title = PageStore.get('config-enabled').pages.history.title,
|
||||
title = translate_string('History'),
|
||||
}) => {
|
||||
const [resultsCount, setResultsCount] = useState<number | null>(null);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { MediaMultiListWrapper } from '../components/MediaMultiListWrapper';
|
|||
import { ItemListAsync } from '../components/item-list/ItemListAsync.jsx';
|
||||
import { InlineSliderItemListAsync } from '../components/item-list/InlineSliderItemListAsync.jsx';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
const EmptyMedia: React.FC = ({}) => {
|
||||
return (
|
||||
|
@ -35,9 +36,12 @@ interface HomePageProps {
|
|||
|
||||
export const HomePage: React.FC<HomePageProps> = ({
|
||||
id = 'home',
|
||||
featured_title = PageStore.get('config-options').pages.home.sections.featured.title,
|
||||
recommended_title = PageStore.get('config-options').pages.home.sections.recommended.title,
|
||||
latest_title = PageStore.get('config-options').pages.home.sections.latest.title,
|
||||
//featured_title = PageStore.get('config-options').pages.home.sections.featured.title,
|
||||
//recommended_title = PageStore.get('config-options').pages.home.sections.recommended.title,
|
||||
//latest_title = PageStore.get('config-options').pages.home.sections.latest.title,
|
||||
featured_title = translate_string('Featured'),
|
||||
recommended_title = translate_string('Recommended'),
|
||||
latest_title = translate_string('Latest'),
|
||||
latest_view_all_link = false,
|
||||
featured_view_all_link = true,
|
||||
recommended_view_all_link = true,
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PageStore } from '../utils/stores/';
|
|||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
interface LatestMediaPageProps {
|
||||
id?: string;
|
||||
|
@ -12,7 +13,7 @@ interface LatestMediaPageProps {
|
|||
|
||||
export const LatestMediaPage: React.FC<LatestMediaPageProps> = ({
|
||||
id = 'latest-media',
|
||||
title = PageStore.get('config-enabled').pages.latest.title,
|
||||
title = translate_string('Recent uploads'),
|
||||
}) => (
|
||||
<Page id={id}>
|
||||
<ApiUrlConsumer>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { MediaListWrapper } from '../components/MediaListWrapper';
|
|||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync';
|
||||
import { ProfileLikedPage } from './ProfileLikedPage';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -21,7 +22,7 @@ interface AnonymousLikedMediaPageProps {
|
|||
|
||||
export const AnonymousLikedMediaPage: React.FC<AnonymousLikedMediaPageProps> = ({
|
||||
id = 'liked-media',
|
||||
title = PageStore.get('config-enabled').pages.liked.title,
|
||||
title = translate_string('Liked media'),
|
||||
}) => {
|
||||
const [resultsCount, setResultsCount] = useState<number | null>(null);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PageStore } from '../utils/stores/';
|
|||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
interface RecommendedMediaPageProps {
|
||||
id?: string;
|
||||
|
@ -12,7 +13,7 @@ interface RecommendedMediaPageProps {
|
|||
|
||||
export const RecommendedMediaPage: React.FC<RecommendedMediaPageProps> = ({
|
||||
id = 'recommended-media',
|
||||
title = PageStore.get('config-enabled').pages.recommended.title,
|
||||
title = translate_string('Recommended'),
|
||||
}) => (
|
||||
<Page id={id}>
|
||||
<ApiUrlConsumer>
|
||||
|
|
|
@ -3,13 +3,14 @@ import { ApiUrlConsumer } from '../utils/contexts/';
|
|||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||
import { LazyLoadItemListAsync } from '../components/item-list/LazyLoadItemListAsync.jsx';
|
||||
import { Page } from './Page';
|
||||
import { translate_string } from '../utils/helpers/';
|
||||
|
||||
interface TagsPageProps {
|
||||
id?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const TagsPage: React.FC<TagsPageProps> = ({ id = 'tags', title = 'Tags' }) => (
|
||||
export const TagsPage: React.FC<TagsPageProps> = ({ id = 'tags', title = translate_string('Tags') }) => (
|
||||
<Page id={id}>
|
||||
<ApiUrlConsumer>
|
||||
{(apiUrl) => (
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { addClassname, removeClassname } from '../helpers/';
|
||||
import { translate_string } from '../../utils/helpers/';
|
||||
|
||||
export function UpNextLoaderView(nextItemData) {
|
||||
var timerTimeout;
|
||||
|
@ -39,7 +40,7 @@ export function UpNextLoaderView(nextItemData) {
|
|||
domElems.nextMediaTitle.setAttribute('class', 'next-media-title');
|
||||
domElems.nextMediaAuthor.setAttribute('class', 'next-media-author');
|
||||
|
||||
domElems.upNextLabel.innerHTML = 'Up Next';
|
||||
domElems.upNextLabel.innerHTML = translate_string('Up Next');
|
||||
domElems.nextMediaTitle.innerHTML = nextItemData.title;
|
||||
domElems.nextMediaAuthor.innerHTML = nextItemData.author_name;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { createContext } from 'react';
|
||||
import { config as mediacmsConfig } from '../settings/config.js';
|
||||
import { translate_string } from '../../utils/helpers/';
|
||||
|
||||
const config = mediacmsConfig(window.MediaCMS);
|
||||
|
||||
|
@ -17,7 +18,7 @@ function popupTopNavItems() {
|
|||
items.push({
|
||||
link: links.user.addMedia,
|
||||
icon: 'video_call',
|
||||
text: 'Upload media',
|
||||
text: translate_string('Upload media'),
|
||||
itemAttr: {
|
||||
className: 'visible-only-in-small',
|
||||
},
|
||||
|
@ -27,7 +28,7 @@ function popupTopNavItems() {
|
|||
items.push({
|
||||
link: user.pages.media,
|
||||
icon: 'video_library',
|
||||
text: 'My media',
|
||||
text: translate_string('My media'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +36,7 @@ function popupTopNavItems() {
|
|||
items.push({
|
||||
link: links.signout,
|
||||
icon: 'exit_to_app',
|
||||
text: 'Sign out',
|
||||
text: translate_string('Sign out'),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -64,7 +65,7 @@ function popupMiddleNavItems() {
|
|||
itemType: 'link',
|
||||
icon: 'login',
|
||||
iconPos: 'left',
|
||||
text: 'Sign in',
|
||||
text: translate_string('Sign in'),
|
||||
link: links.signin,
|
||||
linkAttr: {
|
||||
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
|
||||
|
@ -77,7 +78,7 @@ function popupMiddleNavItems() {
|
|||
itemType: 'link',
|
||||
icon: 'person_add',
|
||||
iconPos: 'left',
|
||||
text: 'Register',
|
||||
text: translate_string('Register'),
|
||||
link: links.register,
|
||||
linkAttr: {
|
||||
className: hasThemeSwitcher ? 'visible-only-in-small' : 'visible-only-in-extra-small',
|
||||
|
@ -88,14 +89,14 @@ function popupMiddleNavItems() {
|
|||
items.push({
|
||||
link: links.user.editProfile,
|
||||
icon: 'brush',
|
||||
text: 'Edit profile',
|
||||
text: translate_string('Edit profile'),
|
||||
});
|
||||
|
||||
if (user.can.changePassword) {
|
||||
items.push({
|
||||
link: links.changePassword,
|
||||
icon: 'lock',
|
||||
text: 'Change password',
|
||||
text: translate_string('Change password'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,3 +12,4 @@ export * from './propTypeFilters';
|
|||
export { default as publishedOnDate } from './publishedOnDate';
|
||||
export * from './quickSort';
|
||||
export * from './requests';
|
||||
export { translate_string } from './translate';
|
||||
|
|
9
frontend/src/static/js/utils/helpers/translate.js
Normal file
9
frontend/src/static/js/utils/helpers/translate.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
// check templates/config/installation/translations.html for more
|
||||
|
||||
export function translate_string(string) {
|
||||
if (window.TRANSLATIONS && window.TRANSLATIONS[string]) {
|
||||
return window.TRANSLATIONS[string];
|
||||
} else {
|
||||
return string;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
65
templates/cms/set_language.html
Normal file
65
templates/cms/set_language.html
Normal file
|
@ -0,0 +1,65 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block headtitle %}About - {{PORTAL_NAME}}{% endblock headtitle %}
|
||||
|
||||
{% block headermeta %}
|
||||
|
||||
<meta property="og:title" content="About - {{PORTAL_NAME}}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:description" content="">
|
||||
|
||||
<meta name="twitter:card" content="summary">
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [{
|
||||
"@type": "ListItem",
|
||||
"position": 1,
|
||||
"name": "{{PORTAL_NAME}}",
|
||||
"item": {
|
||||
"@type": "WebPage",
|
||||
"@id": "{{FRONTEND_HOST}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2,
|
||||
"name": "About",
|
||||
"item": {
|
||||
"@type": "AboutPage",
|
||||
"@id": "{{FRONTEND_HOST}}/about"
|
||||
}
|
||||
}]
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock headermeta %}
|
||||
|
||||
{% block innercontent %}
|
||||
<div class="custom-page-wrapper">
|
||||
<h1>Change Language</h1>
|
||||
<hr/>
|
||||
<h2>Select</h2>
|
||||
|
||||
<p>
|
||||
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
||||
<input name="next" type="hidden" value="/">
|
||||
<select name="language">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,10 +1,11 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
|
||||
var MediaCMS = {
|
||||
{% if media %}mediaId: "{{media}}", {% endif %}
|
||||
{% if user %}profileId: "{{user.username}}", {% endif %}
|
||||
};
|
||||
|
||||
|
||||
{% include "config/core/api.html" %}
|
||||
{% include "config/core/url.html" %}
|
||||
{% include "config/core/user.html" %}
|
||||
|
@ -14,6 +15,8 @@
|
|||
{% include "config/installation/pages.html" %}
|
||||
{% include "config/installation/site.html" %}
|
||||
|
||||
{% include "config/installation/translations.html" %}
|
||||
|
||||
window.MediaCMS = MediaCMS;
|
||||
|
||||
|
||||
</script>
|
||||
|
|
57
templates/config/installation/translations.html
Normal file
57
templates/config/installation/translations.html
Normal file
|
@ -0,0 +1,57 @@
|
|||
// These are the translations that will be used in frontend/src using the helper function
|
||||
// translate_string (frontend/src/static/js/utils/helpers/translate.js)
|
||||
// They are fetched from the backend, and specifically files/frontend_translations
|
||||
// The Django backend is passing dictionary FRONTEND_TRANSLATIONS.
|
||||
// This contains key values where keys do not have a space
|
||||
// TRANSLATIONS object will be availaible as window.TRANSLATIONS
|
||||
|
||||
// IMPORTANT: If you are starting the frontend app (localhost:8088), you are not getting at this
|
||||
// POINT, so there are not translations. Translations can only be available and tested on the Django app
|
||||
// and templates, that are loading React as well
|
||||
|
||||
// TODO: pass the python dictionary on a way that can be parsed here and thus no need
|
||||
// for this duplication taking place here
|
||||
|
||||
var TRANSLATIONS = {
|
||||
"Home": "{{FRONTEND_TRANSLATIONS.Home}}",
|
||||
"Featured": "{{FRONTEND_TRANSLATIONS.Featured}}",
|
||||
"View all": "{{FRONTEND_TRANSLATIONS.View_all}}",
|
||||
"VIEW ALL": "{{FRONTEND_TRANSLATIONS.VIEW_ALL}}",
|
||||
"Latest": "{{FRONTEND_TRANSLATIONS.Latest}}",
|
||||
"Recommended": "{{FRONTEND_TRANSLATIONS.Recommended}}",
|
||||
"Recent uploads": "{{FRONTEND_TRANSLATIONS.Recent_uploads}}",
|
||||
"view": "{{FRONTEND_TRANSLATIONS.view}}",
|
||||
"views": "{{FRONTEND_TRANSLATIONS.views}}",
|
||||
"Search": "{{FRONTEND_TRANSLATIONS.Search}}",
|
||||
|
||||
"Tags": "{{FRONTEND_TRANSLATIONS.Tags}}",
|
||||
"Categories": "{{FRONTEND_TRANSLATIONS.Categories}}",
|
||||
"Members": "{{FRONTEND_TRANSLATIONS.Members}}",
|
||||
"Upload": "{{FRONTEND_TRANSLATIONS.Upload}}",
|
||||
"Upload media": "{{FRONTEND_TRANSLATIONS.Upload_media}}",
|
||||
|
||||
"My media": "{{FRONTEND_TRANSLATIONS.My_media}}",
|
||||
"My playlists": "{{FRONTEND_TRANSLATIONS.My_playlists}}",
|
||||
"History": "{{FRONTEND_TRANSLATIONS.History}}",
|
||||
"Liked media": "{{FRONTEND_TRANSLATIONS.Liked_media}}",
|
||||
"About": "{{FRONTEND_TRANSLATIONS.About}}",
|
||||
"Terms": "{{FRONTEND_TRANSLATIONS.Terms}}",
|
||||
"Contact": "{{FRONTEND_TRANSLATIONS.Contact}}",
|
||||
"Language": "{{FRONTEND_TRANSLATIONS.Language}}",
|
||||
"Manage media": "{{FRONTEND_TRANSLATIONS.Manage_media}}",
|
||||
"Manage users": "{{FRONTEND_TRANSLATIONS.Manage_users}}",
|
||||
"Manage comments": "{{FRONTEND_TRANSLATIONS.Manage_comments}}",
|
||||
|
||||
"Up next": "{{FRONTEND_TRANSLATIONS.Up_next}}",
|
||||
"AUTOPLAY": "{{FRONTEND_TRANSLATIONS.AUTOPLAY}}",
|
||||
|
||||
"Sign out": "{{FRONTEND_TRANSLATIONS.Sign_out}}",
|
||||
"Sign in": "{{FRONTEND_TRANSLATIONS.Sign_in}}",
|
||||
"Register": "{{FRONTEND_TRANSLATIONS.Register}}",
|
||||
"Edit profile": "{{FRONTEND_TRANSLATIONS.Edit_profile}}",
|
||||
"Change password": "{{FRONTEND_TRANSLATIONS.Change_password}}",
|
||||
|
||||
}
|
||||
|
||||
|
||||
window.TRANSLATIONS = TRANSLATIONS;
|
Loading…
Add table
Reference in a new issue