Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Markos Gogoulos
c2b7c2d2fd internalization 2023-11-01 10:06:01 +02:00
Markos Gogoulos
3d0370da34 internalization 2023-11-01 10:05:18 +02:00
51 changed files with 512 additions and 69 deletions

View file

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

View file

@ -13,6 +13,7 @@ schema_view = get_schema_view(
permission_classes=(AllowAny,),
)
# refactor seriously
urlpatterns = [
re_path(r"^__debug__/", include(debug_toolbar.urls)),

View file

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

View file

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

View file

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

View 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

View 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",
}

View 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": "Αλλαγή κωδικού",
}

View 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": "",
}

View 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",
}

View file

@ -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"),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) => (

View file

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

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View file

@ -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) => (

View file

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

View file

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

View file

@ -12,3 +12,4 @@ export * from './propTypeFilters';
export { default as publishedOnDate } from './publishedOnDate';
export * from './quickSort';
export * from './requests';
export { translate_string } from './translate';

View 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

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

View file

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

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