Skip to content

Commit

Permalink
feat: translations support
Browse files Browse the repository at this point in the history
  • Loading branch information
mgogoulos authored Oct 4, 2024
1 parent ef4067c commit 4992cc4
Show file tree
Hide file tree
Showing 84 changed files with 2,303 additions and 161 deletions.
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.PHONY: admin-shell build-frontend

admin-shell:
@container_id=$$(docker-compose ps -q web); \
if [ -z "$$container_id" ]; then \
echo "Web container not found"; \
exit 1; \
else \
docker exec -it $$container_id /bin/bash; \
fi

build-frontend:
docker-compose -f docker-compose-dev.yaml exec frontend npm run dist
cp -r frontend/dist/static/* static/
docker-compose -f docker-compose-dev.yaml restart web
16 changes: 4 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,20 @@ A demo is available at https://demo.mediacms.io
- **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers
- **Chunked file uploads**: for pausable/resumable upload of content
- **REST API**: Documented through Swagger
- **Translation**: Most of the CMS is translated to a number of languages


## Example cases

- **Schools, education.** Administrators and editors keep what content will be published, students are not distracted with advertisements and irrelevant content, plus they have the ability to select either to stream or download content.

- **Organization sensitive content.** In cases where content is sensitive and cannot be uploaded to external sites.

- **Build a great community.** MediaCMS can be customized (URLs, logos, fonts, aesthetics) so that you create a highly customized video portal for your community!

- **Personal portal.** Organize, categorize and host your content the way you prefer.


## Philosophy

We believe there's a need for quality open source web applications that can be used to build community portals and support collaboration.

We have three goals for MediaCMS: a) deliver all functionality one would expect from a modern system, b) allow for easy installation and maintenance, c) allow easy customization and addition of features.


Expand All @@ -80,17 +77,12 @@ For a small to medium installation, with a few hours of video uploaded daily, an
In terms of disk space, think of what the needs will be. A general rule is to multiply by three the size of the expected uploaded videos (since the system keeps original versions, encoded versions plus HLS), so if you receive 1G of videos daily and maintain all of them, you should consider a 1T disk across a year (1G * 3 * 365).


## Releases

Visit [Releases Page](https://github.com/mediacms-io/mediacms/releases) for detailed Changelog


## Installation / Maintanance

There are two ways to run MediaCMS, through Docker Compose and through installing it on a server via an automation script that installs and configures all needed services. Find the related pages:

* [Single Server](docs/admins_docs.md#2-server-installation) page
* [Docker Compose](docs/admins_docs.md#3-docker-installation) page
- [Single Server](docs/admins_docs.md#2-server-installation) page
- [Docker Compose](docs/admins_docs.md#3-docker-installation) page

A complete guide can be found on the blog post [How to self-host and share your videos in 2021](https://medium.com/@MediaCMS.io/how-to-self-host-and-share-your-videos-in-2021-14067e3b291b).

Expand Down Expand Up @@ -119,7 +111,7 @@ This software uses the following list of awesome technologies: Python, Django, D

- **Cinemata** non-profit media, technology and culture organization - https://cinemata.org
- **Critical Commons** public media archive and fair use advocacy network - https://criticalcommons.org
- **American Association of Gynecologic Laparoscopists** - https://surgeryu.aagl.org/
- **American Association of Gynecologic Laparoscopists** - https://surgeryu.org/


## How to contribute
Expand Down
1 change: 1 addition & 0 deletions cms/dev_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
'corsheaders.middleware.CorsMiddleware',
'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',
Expand Down
24 changes: 24 additions & 0 deletions cms/settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os

from celery.schedules import crontab
from django.utils.translation import gettext_lazy as _

DEBUG = False

Expand Down Expand Up @@ -311,6 +312,7 @@
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",
Expand Down Expand Up @@ -507,3 +509,25 @@
from .dev_settings import * # noqa
except ImportError:
pass

LANGUAGES = [
('ar', _('Arabic')),
('bn', _('Bengali')),
('nl', _('Dutch')),
('en', _('English')),
('fr', _('French')),
('de', _('German')),
('hi', _('Hindi')),
('id', _('Indonesian')),
('ja', _('Japanese')),
('ko', _('Korean')),
('pt', _('Portuguese')),
('ru', _('Russian')),
('zh-hans', _('Simplified Chinese')),
('es', _('Spanish')),
('tr', _('Turkish')),
('el', _('Greek')),
('ur', _('Urdu')),
]

LANGUAGE_CODE = 'en' # default language
1 change: 1 addition & 0 deletions cms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
permission_classes=(AllowAny,),
)

# refactor seriously

urlpatterns = [
re_path(r"^__debug__/", include(debug_toolbar.urls)),
Expand Down
39 changes: 39 additions & 0 deletions docs/admins_docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [17. Cookie consent code](#17-cookie-consent-code)
- [18. Disable encoding and show only original file](#18-disable-encoding-and-show-only-original-file)
- [19. Rounded corners on videos](#19-rounded-corners)
- [20. Translations](#20-translations)

## 1. Welcome
This page is created for MediaCMS administrators that are responsible for setting up the software, maintaining it and making modifications.
Expand Down Expand Up @@ -801,3 +802,41 @@ frontend/src/static/js/components/list-item/Item.scss
frontend/src/static/js/components/media-page/MediaPage.scss
```
you now have to re-run the frontend build in order to see the changes (check docs/dev_exp.md)


## 20. Translations

### 20.1 Set a default language

By default MediaCMS is available in a number of languages. To set the default language, edit `settings.py` and set LANGUAGE_CODE to the code of one of the languages.

### 20.2 Remove existing languages
To limit the number of languages that are shown as available, remove them from the LANGUAGES list in `settings.py` or comment them. Only what is there is shown.

### 20.3 Improve existing translation
To make improvements in existing translated content, in a language that is already translated, check the language by the code name in `files/frontend-translations/` and edit the corresponding file.

### 20.4 Add more content to existing translation
Not all text is translated, so at any time you may find strings missing that need to be added to the translation. The idea here is that

a) you made the text as translatable, in the code
b) you add the translated string

For a), you have to see if the string to be translated lives in the frontend directory (React app) or on the Django templates. There are examples for both.
1. the Django templates, which is found in templates/ dir. Have a look on `templates/cms/about.html` to see an example of how it is done
2. the frontend code (React), have a look how `translateString` is used in `frontend`


After the string is marked as translatable, add the string to `files/frontend-translations/en.py` first, and then run

```
python manage.py process_translations
```

In order to populate the string in all the languages. NO PR will be accepted if this procedure is not followed. You don't have to translate the string to all supported languages, but the command has to run and populate the existing dictionaries with the new strings for all languages. This ensures that there is no missing string to be translated in any language.

After this command is run, translate the string to the language you want. If the string to be translated lives in Django templates, you don't have to re-build the frontend. If the change lives in the frontend, you will have to re-build in order to see the changes. The Makefile command `make build-frontend` can help with this.


### 20.5 Add a new language and translate
To add a new language: add the language in settings.py, then add the file in `files/frontend-translations/`. Make sure you copy the initial strings by copying `files/frontend-translations/en.py` to it.
31 changes: 30 additions & 1 deletion docs/dev_exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ Check it on http://localhost:8088/
### How to develop in Django
Django starts at http://localhost and is reloading automatically. Making any change to the python code should refresh Django.

If Django breaks due to an error (eg SyntaxError, while editing the code), you might have to restart it

```
docker-compose -f docker-compose-dev.yaml restart web
```



### How to develop in React
React is started on http://localhost:8088/ , code is located in frontend/ , so making changes there should have instant effect on the page. Keep in mind that React is loading data from Django, and that it has to be built so that Django can serve it.

Expand All @@ -57,4 +65,25 @@ In order to make changes to React code, edit code on frontend/src and check it's
3. Build frontend with `docker-compose -f docker-compose-dev.yaml exec frontend npm run dist`
4. Copy static files to Django static folder with`cp -r frontend/dist/static/* static/`
5. Restart Django - `docker-compose -f docker-compose-dev.yaml restart web` so that it uses the new static files
6. Commit the changes
6. Commit the changes

### Helper commands
There is ongoing effort to provide helper commands, check the Makefile for what it supports. Eg

Bash into the web container:

```
user@user:~/mediacms$ make admin-shell
root@ca8c1096726b:/home/mediacms.io/mediacms# ./manage.py shell
```

Build the frontend:

```
user@user:~/mediacms$ make build-frontend
docker-compose -f docker-compose-dev.yaml exec frontend npm run dist
> [email protected] dist /home/mediacms.io/mediacms/frontend
> mediacms-scripts rimraf ./dist && mediacms-scripts build --config=./config/mediacms.config.js --env=dist
...
```
11 changes: 1 addition & 10 deletions files/admin.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
from django.contrib import admin

from .models import (
Category,
Comment,
EncodeProfile,
Encoding,
Language,
Media,
Subtitle,
Tag,
)
from .models import Category, Comment, EncodeProfile, Encoding, Language, Media, Subtitle, Tag


class CommentAdmin(admin.ModelAdmin):
Expand Down
4 changes: 4 additions & 0 deletions files/context_processors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.conf import settings

from .frontend_translations import get_translation, get_translation_strings
from .methods import is_mediacms_editor, is_mediacms_manager


Expand Down Expand Up @@ -31,4 +32,7 @@ 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["TRANSLATION"] = get_translation(request.LANGUAGE_CODE)
ret["REPLACEMENTS"] = get_translation_strings(request.LANGUAGE_CODE)

return ret
61 changes: 61 additions & 0 deletions files/frontend_translations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import importlib
import os

from django.conf import settings

current_dir = os.path.dirname(os.path.abspath(__file__))
files = os.listdir(current_dir)
translation_strings = {}
replacement_strings = {}


def check_language_code(language_code):
# helper function
if language_code not in [pair[0] for pair in settings.LANGUAGES]:
return False
if language_code in ['en', 'en-us', 'en-gb']:
return False
return True


for translation_file in files:
# the language code is zh-hans but the file is zh_hans.py

language_code_file = translation_file.split('.')[0]
language_code = language_code_file.replace('_', '-')
if not check_language_code(language_code):
continue

module_name = f"files.frontend_translations.{language_code_file}"
tr_module = importlib.import_module(module_name)
translation_strings[language_code] = tr_module.translation_strings
replacement_strings[language_code] = tr_module.replacement_strings



def get_translation(language_code):
# get list of translations per language
if not check_language_code(language_code):
return {}

translation = translation_strings[language_code]

return translation


def get_translation_strings(language_code):
# get list of replacement strings per language
if not check_language_code(language_code):
return {}

translation = replacement_strings[language_code]

return translation


def translate_string(language_code, string):
# translate a string to the given language
if not check_language_code(language_code):
return string

return translation_strings[language_code].get(string, string)
Loading

0 comments on commit 4992cc4

Please sign in to comment.