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

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

parent 997412d7
No related branches found
No related tags found
1 merge request!1086Resolve "Support i18n directly in Vue"
......@@ -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 graphene_django.forms.mutation import DjangoModelFormMutation
from .forms import PersonForm
from .models import Group, 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):
......@@ -27,6 +32,15 @@ class GroupType(DjangoObjectType):
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 PersonMutation(DjangoModelFormMutation):
person = graphene.Field(PersonType)
......@@ -59,6 +73,9 @@ class Query(graphene.ObjectType):
person_by_id = graphene.Field(PersonType, id=graphene.ID())
who_am_i = graphene.Field(PersonType)
language = graphene.String()
available_languages = graphene.List(LanguageType)
def resolve_notifications(root, info, **kwargs):
# FIXME do permission stuff
return Notification.objects.all()
......@@ -76,6 +93,15 @@ class Query(graphene.ObjectType):
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):
update_person = PersonMutation.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