diff --git a/moku/config/settings.py b/moku/config/settings.py index 8cf482a..b4aeab3 100644 --- a/moku/config/settings.py +++ b/moku/config/settings.py @@ -35,6 +35,7 @@ MIDDLEWARE = [ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "moku.middleware.MokuLanguageMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] diff --git a/moku/config/urls.py b/moku/config/urls.py index b203ac6..fbdbcd6 100644 --- a/moku/config/urls.py +++ b/moku/config/urls.py @@ -21,7 +21,7 @@ from django.urls import include, path from moku.views.auth import LoginView, LogoutView from moku.views.post import FeedView -from moku.views.user import EditProfileView, ProfileView, SignupView +from moku.views.user import EditProfileView, EditSettingsView, ProfileView, SignupView urlpatterns = [ path("admin/", admin.site.urls), @@ -30,6 +30,7 @@ urlpatterns = [ path("logout", LogoutView.as_view(), name="logout"), path("signup", SignupView.as_view(), name="signup"), path("profile", EditProfileView.as_view(), name="profile.edit"), + path("settings", EditSettingsView.as_view(), name="settings"), path("user/", ProfileView.as_view(), name="profile"), ] diff --git a/moku/forms/user.py b/moku/forms/user.py index 08500ab..5122278 100644 --- a/moku/forms/user.py +++ b/moku/forms/user.py @@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _ from django_recaptcha.fields import ReCaptchaField -from moku.models.user import User +from moku.models.user import User, UserSettings class UserForm(UserCreationForm): @@ -31,6 +31,15 @@ class UserForm(UserCreationForm): self.fields["captcha"].error_messages = {"required": _("make sure you've ticked the captcha.")} +class UserSettingsForm(forms.ModelForm): + class Meta: + model = UserSettings + fields = ("language",) + labels = { + "language": _("language"), + } + + class ProfileForm(forms.ModelForm): class Meta: model = User diff --git a/moku/middleware.py b/moku/middleware.py new file mode 100644 index 0000000..df9b010 --- /dev/null +++ b/moku/middleware.py @@ -0,0 +1,11 @@ +from django.utils import translation + + +class MokuLanguageMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if request.user.is_authenticated and hasattr(request.user, "settings"): + translation.activate(request.user.settings.language) + return self.get_response(request) diff --git a/moku/migrations/0005_usersettings.py b/moku/migrations/0005_usersettings.py new file mode 100644 index 0000000..65bc607 --- /dev/null +++ b/moku/migrations/0005_usersettings.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.3 on 2024-03-25 17:02 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('moku', '0004_alter_post_verb'), + ] + + operations = [ + migrations.CreateModel( + name='UserSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(choices=[('af', 'Afrikaans'), ('ar', 'Arabic'), ('ar-dz', 'Algerian Arabic'), ('ast', 'Asturian'), ('az', 'Azerbaijani'), ('bg', 'Bulgarian'), ('be', 'Belarusian'), ('bn', 'Bengali'), ('br', 'Breton'), ('bs', 'Bosnian'), ('ca', 'Catalan'), ('ckb', 'Central Kurdish (Sorani)'), ('cs', 'Czech'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('dsb', 'Lower Sorbian'), ('el', 'Greek'), ('en', 'English'), ('en-au', 'Australian English'), ('en-gb', 'British English'), ('eo', 'Esperanto'), ('es', 'Spanish'), ('es-ar', 'Argentinian Spanish'), ('es-co', 'Colombian Spanish'), ('es-mx', 'Mexican Spanish'), ('es-ni', 'Nicaraguan Spanish'), ('es-ve', 'Venezuelan Spanish'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('fi', 'Finnish'), ('fr', 'French'), ('fy', 'Frisian'), ('ga', 'Irish'), ('gd', 'Scottish Gaelic'), ('gl', 'Galician'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('hr', 'Croatian'), ('hsb', 'Upper Sorbian'), ('hu', 'Hungarian'), ('hy', 'Armenian'), ('ia', 'Interlingua'), ('id', 'Indonesian'), ('ig', 'Igbo'), ('io', 'Ido'), ('is', 'Icelandic'), ('it', 'Italian'), ('ja', 'Japanese'), ('ka', 'Georgian'), ('kab', 'Kabyle'), ('kk', 'Kazakh'), ('km', 'Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('ky', 'Kyrgyz'), ('lb', 'Luxembourgish'), ('lt', 'Lithuanian'), ('lv', 'Latvian'), ('mk', 'Macedonian'), ('ml', 'Malayalam'), ('mn', 'Mongolian'), ('mr', 'Marathi'), ('ms', 'Malay'), ('my', 'Burmese'), ('nb', 'Norwegian Bokmål'), ('ne', 'Nepali'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('os', 'Ossetic'), ('pa', 'Punjabi'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('pt-br', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('sq', 'Albanian'), ('sr', 'Serbian'), ('sr-latn', 'Serbian Latin'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('ta', 'Tamil'), ('te', 'Telugu'), ('tg', 'Tajik'), ('th', 'Thai'), ('tk', 'Turkmen'), ('tr', 'Turkish'), ('tt', 'Tatar'), ('udm', 'Udmurt'), ('ug', 'Uyghur'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('uz', 'Uzbek'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese'), ('zh-hant', 'Traditional Chinese')], default='en', help_text='what language do you want to use moku.blog in?', max_length=16, verbose_name='language')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='settings', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/moku/models/user.py b/moku/models/user.py index efce68f..dcef0ae 100644 --- a/moku/models/user.py +++ b/moku/models/user.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import AbstractUser, BaseUserManager +from django.conf import settings +from django.contrib.auth.models import AbstractUser from django.db import models from django.utils.translation import gettext_lazy as _ from django.urls import reverse @@ -77,3 +78,18 @@ class User(AbstractUser): @property def email_confirmed(self): return self.email_confirmed_at is not None + + +class UserSettings(models.Model): + user = models.OneToOneField( + "User", + related_name="settings", + on_delete=models.CASCADE, + ) + language = models.CharField( + verbose_name=_("language"), + max_length=16, + choices=settings.LANGUAGES, + default="en", + help_text=_("what language do you want to use moku.blog in?"), + ) diff --git a/moku/templates/moku/base.jinja b/moku/templates/moku/base.jinja index f8e7b88..9762015 100644 --- a/moku/templates/moku/base.jinja +++ b/moku/templates/moku/base.jinja @@ -15,6 +15,7 @@ {% if request.user.is_authenticated %}
  • {% trans %}feed{% endtrans %}
  • {% trans %}my profile{% endtrans %}
  • +
  • {% trans %}settings{% endtrans %}
  • {% csrf_token %} diff --git a/moku/templates/moku/settings.jinja b/moku/templates/moku/settings.jinja new file mode 100644 index 0000000..e25fb71 --- /dev/null +++ b/moku/templates/moku/settings.jinja @@ -0,0 +1,15 @@ +{% extends "moku/base.jinja" %} + +{% block content %} +
    + + {% csrf_token %} +
    + + {{ form.language }} + {{ form.language.help_text }} +
    + + +
    +{% endblock content %} \ No newline at end of file diff --git a/moku/views/user.py b/moku/views/user.py index bd3ec09..5d662bf 100644 --- a/moku/views/user.py +++ b/moku/views/user.py @@ -1,11 +1,12 @@ from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin +from django.db import IntegrityError from django.shortcuts import redirect from django.utils.translation import gettext as _ from django.views.generic import FormView, TemplateView from moku.images import process_avatar_image -from moku.forms.user import ProfileForm, UserForm +from moku.forms.user import ProfileForm, UserForm, UserSettingsForm from moku.models.user import User @@ -24,6 +25,26 @@ class EditProfileView(LoginRequiredMixin, FormView): return self.form_class(instance=self.request.user, **self.get_form_kwargs()) +class EditSettingsView(LoginRequiredMixin, FormView): + template_name = "moku/settings.jinja" + form_class = UserSettingsForm + + def form_valid(self, form): + form.instance.user = self.request.user + try: + form.save() + except IntegrityError: + messages.error(self.request, _("uh oh. i think something went a little bit oopsie.")) + return self.form_invalid(form) + messages.success(self.request, _("settings updated!")) + return redirect("settings") + + def get_form(self): + if hasattr(self.request.user, "settings"): + return self.form_class(instance=self.request.user.settings, **self.get_form_kwargs()) + return super().get_form() + + class ProfileView(TemplateView): template_name = "moku/profile/show.jinja"