Skip to content
Snippets Groups Projects
Verified Commit 038e2a2b authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Make language settings accessible through GraphQL and set locale directly in Vue

(cherry picked from commit 857f9cd8)
parent 36cfac22
No related branches found
No related tags found
No related merge requests found
......@@ -5,6 +5,7 @@ import "vuetify/dist/vuetify.min.css"
import ApolloClient from 'apollo-boost'
import VueApollo from 'vue-apollo'
import gql from 'graphql-tag'
import "./css/global.scss"
import VueI18n from 'vue-i18n'
......@@ -14,9 +15,8 @@ import messages from "./messages.json"
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: JSON.parse(document.getElementById("current-language").textContent),
locale: "en",
fallbackLocale: "en",
availableLocales: JSON.parse(document.getElementById("language-info-list").textContent),
messages
});
......@@ -64,10 +64,6 @@ const vuetify = new Vuetify({
},
},
},
lang: {
locales: JSON.parse(document.getElementById("language-info-list").textContent),
current: JSON.parse(document.getElementById("current-language").textContent),
}
})
const apolloClient = new ApolloClient({
......@@ -107,8 +103,16 @@ const app = new Vue({
django: window.django,
// FIXME: maybe just use window.django in every component or find a suitable way to access this property everywhere
showCacheAlert: false,
languageCode: JSON.parse(document.getElementById("current-language").textContent),
}),
apollo: {
language: gql`{ language }`,
},
watch: {
language: function (newLanguage) {
this.$i18n.locale = newLanguage;
this.$vuetify.lang.current = newLanguage;
}
},
components: {
"cache-notification": CacheNotification,
"language-form": LanguageForm,
......
<template>
<form method="post" ref="form" :action="action" id="language-form">
<v-text-field
v-show="false"
name="csrfmiddlewaretoken"
:value="csrf_value"
type="hidden"
></v-text-field>
<v-text-field
v-show="false"
name="next"
:value="next_url"
type="hidden"
></v-text-field>
<v-text-field
v-show="false"
v-model="language"
name="language"
type="hidden"
></v-text-field>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn
depressed
v-bind="attrs"
v-on="on"
color="primary"
>
<v-icon icon color="white">mdi-translate</v-icon>
{{ language }}
</v-btn>
</template>
<v-list id="language-dropdown" class="dropdown-content">
<v-list-item-group
v-model="language"
color="primary"
>
<v-list-item v-for="language_option in items" :key="language_option[0]" :value="language_option[0]" @click="submit(language_option[0])">
<v-list-item-title>{{ language_option[1] }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
</form>
>
<v-icon icon color="white">mdi-translate</v-icon>
{{ $i18n.locale }}
</v-btn>
</template>
<v-list id="language-dropdown" class="dropdown-content" min-width="150">
<ApolloQuery :query="require('./availableLanguages.graphql')">
<template v-slot="{ result: { error, data }, isLoading }">
<v-skeleton-loader
v-if="isLoading"
class="mx-auto"
type="list-item, list-item, list-item"
></v-skeleton-loader>
<v-list-item-group
v-if="!isLoading"
v-model="$i18n.locale"
color="primary"
>
<v-list-item v-for="languageOption in data.availableLanguages" :key="languageOption.code"
:value="languageOption.code"
@click="setLanguage(languageOption)">
<v-list-item-title>{{ languageOption.nameTranslated }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</template>
</ApolloQuery>
</v-list>
</v-menu>
</template>
<script>
export default {
data: () => ({
items: JSON.parse(document.getElementById("language-info-list").textContent),
language: JSON.parse(document.getElementById("current-language").textContent),
}),
methods: {
submit: function (new_language) {
this.language = new_language;
this.$nextTick(() => {
this.$refs.form.submit();
});
},
export default {
data: function () {
return {
language: this.$i18n.locale
}
},
methods: {
setLanguage: function (languageOption) {
document.cookie = languageOption.cookie;
this.$i18n.locale = languageOption.code;
this.$vuetify.lang.current = languageOption.code;
},
props: ["action", "csrf_value", "next_url"],
name: "language-form",
}
},
name: "language-form",
}
</script>
{
availableLanguages {
code
nameTranslated
cookie
}
}
from django.conf import settings
from django.utils import translation
import graphene
from graphene import ObjectType
from graphene_django import DjangoObjectType
from .models import Notification, Person
from .util.core_helpers import get_app_module, get_app_packages, has_person
from .util.frontend_helpers import get_language_cookie
class NotificationType(DjangoObjectType):
......@@ -20,6 +25,20 @@ class PersonType(DjangoObjectType):
return root.full_name
class GroupType(DjangoObjectType):
class Meta:
model = Group
class LanguageType(ObjectType):
code = graphene.String(required=True)
name = graphene.String(required=True)
name_local = graphene.String(required=True)
name_translated = graphene.String(required=True)
bidi = graphene.Boolean(required=True)
cookie = graphene.String(required=True)
class MarkNotificationReadMutation(graphene.Mutation):
class Arguments:
id = graphene.ID() # noqa
......@@ -41,12 +60,24 @@ class Query(graphene.ObjectType):
who_am_i = graphene.Field(PersonType)
language = graphene.String()
available_languages = graphene.List(LanguageType)
def resolve_who_am_i(root, info, **kwargs):
if has_person(info.context.user):
return info.context.user.person
else:
return None
def resolve_language(root, info, **kwargs):
return info.context.LANGUAGE_CODE
def resolve_available_languages(root, info, **kwargs):
return [
translation.get_language_info(code) | {"cookie": get_language_cookie(code)}
for code, name in settings.LANGUAGES
]
class Mutation(graphene.ObjectType):
mark_notification_read = MarkNotificationReadMutation.Field()
......
......@@ -98,8 +98,7 @@
</v-toolbar-title>
<v-spacer></v-spacer>
<language-form action="{% url "set_language" %}"
csrf_value={{ csrf_token }} next_url={{ request.get_full_path }}></language-form>
<language-form></language-form>
{% if user.is_authenticated %}
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
......@@ -231,8 +230,6 @@
{{ request.user.person.preferences.theme__design|json_script:"design-mode" }}
{{ request.site.preferences.theme__primary|json_script:"primary-color" }}
{{ request.site.preferences.theme__secondary|json_script:"secondary-color" }}
{{ LANGUAGE_CODE|json_script:"current-language" }}
{{ LANGUAGES|json_script:"language-info-list" }}
<script type="text/javascript" src="{% static 'js/search.js' %}"></script>
{% render_bundle 'core' %}
{% block extra_body %}{% endblock %}
......
import os
from django.conf import settings
from .core_helpers import get_app_module, get_app_packages
......@@ -13,3 +15,18 @@ def get_apps_with_assets():
package = ".".join(app.split(".")[:-2])
assets[package] = path
return assets
def get_language_cookie(code: str) -> str:
"""Build a cookie string to set a new language."""
cookie_parts = [f"{settings.LANGUAGE_COOKIE_NAME}={code}"]
args = dict(
max_age=settings.LANGUAGE_COOKIE_AGE,
path=settings.LANGUAGE_COOKIE_PATH,
domain=settings.LANGUAGE_COOKIE_DOMAIN,
secure=settings.LANGUAGE_COOKIE_SECURE,
httponly=settings.LANGUAGE_COOKIE_HTTPONLY,
samesite=settings.LANGUAGE_COOKIE_SAMESITE,
)
cookie_parts += [f"{k.replace('_', '-')}={v}" for k, v in args.items() if v]
return "; ".join(cookie_parts)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment