Add locize cli script

This commit is contained in:
Daniel Egger 2023-07-05 16:15:17 +02:00
parent 828ea32a46
commit 6e7935a005
13 changed files with 1064 additions and 52 deletions

View File

@ -101,19 +101,35 @@ Preferences -> Tools -> Actions on Save
## Translations
We use (vue-i18n)[https://kazupon.github.io/vue-i18n/] for translations
and (vue-i18n-extract)[https://github.com/Spittal/vue-i18n-extract] for helper
scripts.
We use (Locize)[https://locize.com] together with (i18next)[https://www.i18next.com/]
for translations on the Frontend.
```
# will create a report on command line with missing translations
npm run vue-i18n-extract
The master for translated files is on Locize. Missing keys get automatically added to
Locize, no manual step is needed.
The translations are done on Locize.
# add missing translations to files, see docs for more options
cd client
npx vue-i18n-extract --add
Please make sure that the required environment variables are set
(see ./env_secrets/local_daniel.env for the values):
* LOCIZE_PROJECT_ID
* LOCIZE_API_KEY
The files in ./client/locales are only used as reference and are not the master!
But you can still sync the local locale files with Locize with the following command.
Please be careful and do not lose translations. The savest way is to only add the
keys to the German file and let Locize translate them.
```bash
npm run locize:sync
```
### "_many" plural form in French and Italian
See https://github.com/i18next/i18next/issues/1691#issuecomment-968063348
for an explanation why this plural form is needed in French and Italian.
But not in German and English.
## Deployment to CapRover
### CapRover Dev (vbv-lernwelt.control.iterativ.ch)

View File

@ -1,6 +1,6 @@
import { Preview, setup } from "@storybook/vue3";
import { createI18n } from "vue-i18n";
import de from "../src/locales/de.json";
import de from "../src/locales/de/translation.json";
import "../tailwind.css";
import { withVueRouter } from "./mockRouter";

958
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,8 @@
"storybook": "storybook dev -p 6006",
"tailwind": "tailwindcss -i tailwind.css -o ../server/vbv_lernwelt/static/css/tailwind.css --watch",
"test": "vitest run",
"typecheck": "npm run codegen && vue-tsc --noEmit -p tsconfig.app.json --composite false"
"typecheck": "npm run codegen && vue-tsc --noEmit -p tsconfig.app.json --composite false",
"locize:sync": "locize sync --path ./src/locales"
},
"dependencies": {
"@headlessui/tailwindcss": "^0.1.3",
@ -35,6 +36,7 @@
"graphql": "^16.6.0",
"i18next-locize-backend": "^6.2.2",
"i18next-vue": "^2.2.0",
"locize": "^2.4.6",
"lodash": "^4.17.21",
"loglevel": "^1.8.0",
"mitt": "^3.0.0",
@ -79,6 +81,7 @@
"eslint-plugin-storybook": "^0.6.12",
"eslint-plugin-vue": "^9.15.0",
"jsdom": "^22.1.0",
"locize-cli": "^7.14.6",
"postcss": "^8.4.14",
"postcss-import": "^15.1.0",
"prettier": "^2.8.8",

View File

@ -1,9 +1,17 @@
import type { AvailableLanguages } from "@/stores/user";
import i18next from "i18next";
import { locizePlugin } from "locize";
import Backend from "i18next-locize-backend";
import { nextTick } from "vue";
declare module "i18next" {
interface CustomTypeOptions {
returnNull: false;
}
}
export const SUPPORT_LOCALES: AvailableLanguages[] = ["de", "fr", "it"];
export function i18nextInit() {
@ -15,13 +23,16 @@ export function i18nextInit() {
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.use(Backend)
.use(locizePlugin)
.init({
debug: true,
fallbackLng: "de",
defaultNS: "translation",
returnNull: false,
saveMissing: true,
backend: {
projectId: "7518c269-cbf7-4d25-bc5c-6ceba2a8b74b",
apiKey: "0001d5bd-04c9-4ade-bedd-308a01d86860",
projectId: import.meta.env.VITE_LOCIZE_PROJECTID,
apiKey: import.meta.env.VITE_LOCIZE_API_KEY,
},
})
);

View File

@ -1,14 +1,9 @@
{
"Benutzername": "Benutzername",
"Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle.": "Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle.",
"Klicke auf den Button, um dich über SSO anzumelden oder zu registrieren.": "Klicke auf den Button, um dich über SSO anzumelden oder zu registrieren.",
"MS Teams öffnen": "MS Teams öffnen",
"Nächste Termine:": "Nächste Termine:",
"Passwort": "Passwort",
"SSO Login/Registration": "SSO Login/Registration",
"Trainerunterlagen": "Trainerunterlagen",
"Zur Zeit sind keine Termine vorhanden": "Zur Zeit sind keine Termine vorhanden",
"welcome": "Hallo Welt",
"assignment": {
"acceptConditionsDisclaimer": "Bedingungen akzeptieren und Ergebnisse abgeben",
"assessmentDocumentDisclaimer": "Diese geleitete Fallarbeit wird auf Grund des folgenden Beurteilungsinstrument bewertet:",
@ -69,7 +64,8 @@
"profileLink": "Profil anzeigen",
"progress": "Teilnehmende / Status",
"tasksDone": "Erledigte Transferaufträge von Teilnehmer.",
"title": "Cockpit"
"title": "Cockpit",
"trainerFilesText": "Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle."
},
"competences": {
"assessAgain": "Sich nochmals einschätzen",
@ -92,6 +88,9 @@
"nocourses": "Du wurdest noch keinem Lehrgang zugewiesen.",
"welcome": "Willkommen, {{name}}"
},
"dueDates": {
"nextDueDates": "Nächste Termine"
},
"feedback": {
"answers": "Antworten",
"areYouSatisfied": "Wie zufrieden bist du?",
@ -143,10 +142,10 @@
"certificate_other": "Zertifikate",
"circles": "Circles",
"close": "Schliessen",
"exam_one": "Prüfung",
"exam_other": "Prüfungen",
"examResult_one": "Prüfungsresultat",
"examResult_other": "Prüfungsresultate",
"exam_one": "Prüfung",
"exam_other": "Prüfungen",
"feedback_one": "Feedback",
"feedback_other": "Feedbacks",
"introduction": "Einleitung",
@ -190,6 +189,12 @@
"topics": "Themen:",
"welcomeBack": "Willkommen zurück in deinem Lehrgang:"
},
"login": {
"demoLogin": "Demo Login",
"login": "Login",
"ssoLogin": "SSO Login/Registration",
"ssoText": "Klicke auf den Button, um dich über SSO anzumelden oder zu registrieren."
},
"mainNavigation": {
"logout": "Abmelden",
"profile": "Profil"
@ -231,5 +236,6 @@
},
"settings": {
"emailNotifications": "Email Benachrichtigungen"
}
},
"welcome": "Hallo Welt"
}

View File

@ -1,11 +1,7 @@
{
"Benutzername": "Nom dutilisateur",
"Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle.": "Tu trouves ici les documents de formation (feuilles de solution, présentations, etc.) pour ton cercle.",
"Klicke auf den Button, um dich über SSO anzumelden oder zu registrieren.": "Clique sur le bouton pour te connecter via le SSO ou tinscrire.",
"MS Teams öffnen": "Ouvrir MS Teams ",
"Nächste Termine:": "Prochaines réunions :",
"Passwort": "Mot de passe",
"SSO Login/Registration": "Connexion SSO / Inscription",
"Trainerunterlagen": "Documents du formateur / de la formatrice",
"Zur Zeit sind keine Termine vorhanden": "Aucune réunion nest prévue pour le moment",
"assignment": {
@ -68,7 +64,8 @@
"profileLink": "Afficher le profil",
"progress": "Personne participante / Statut",
"tasksDone": "Exercices dapplication terminés par les participants.",
"title": "Cockpit"
"title": "Cockpit",
"trainerFilesText": "Tu trouves ici les documents de formation (feuilles de solution, présentations, etc.) pour ton cercle."
},
"competences": {
"assessAgain": "Sévaluer à nouveau",
@ -91,6 +88,9 @@
"nocourses": "Tu nas été affecté(e) à aucune formation encore.",
"welcome": "Bienvenue, {{name}}"
},
"dueDates": {
"nextDueDates": "Prochaines réunions"
},
"feedback": {
"answers": "Réponses",
"areYouSatisfied": "Quel est ton degré de satisfaction ?",
@ -138,14 +138,18 @@
"backCapitalized": "@.capitalize:general.back",
"backToCircle": "Revenir au cercle",
"backToLearningPath": "Revenir au programme de formation",
"certificate_many": "Certificats",
"certificate_one": "Certificat",
"certificate_other": "Certificats",
"circles": "Cercles",
"close": "Fermer",
"exam_one": "Examen",
"exam_other": "Examens",
"examResult_many": "Résultats de lexamen",
"examResult_one": "Résultat de lexamen",
"examResult_other": "Résultats de lexamen",
"exam_many": "Examens",
"exam_one": "Examen",
"exam_other": "Examens",
"feedback_many": "Feed-backs",
"feedback_one": "Feed-back",
"feedback_other": "Feed-backs",
"introduction": "Introduction",
@ -155,6 +159,7 @@
"next": "Continuer",
"nextStep": "Cela continue",
"no": "Non",
"notification_many": "Notifications",
"notification_one": "Notification",
"notification_other": "Notifications",
"profileLink": "Détails",
@ -167,6 +172,7 @@
"start": "Cest parti !",
"submission": "Remise",
"title": "myAFA",
"transferTask_many": "Exercices dapplication",
"transferTask_one": "Exercice dapplication",
"transferTask_other": "Exercices dapplication",
"yes": "Oui"
@ -189,6 +195,12 @@
"topics": "Thèmes :",
"welcomeBack": "Cela fait plaisir de te revoir dans ta formation :"
},
"login": {
"demoLogin": "Connexion Demo",
"login": "Login",
"ssoLogin": "Connexion SSO / Inscription",
"ssoText": "Clique sur le bouton pour te connecter via le SSO ou tinscrire."
},
"mainNavigation": {
"logout": "Se déconnecter",
"profile": "Profil"
@ -196,6 +208,7 @@
"mediaLibrary": {
"handlungsfelder": {
"description": "Trouve toutes les ressources des champs daction, comme les outils didactiques, les liens et autres informations utiles.",
"title_many": "Champs daction",
"title_one": "Champ daction",
"title_other": "Champs daction"
},

View File

@ -1,11 +1,7 @@
{
"Benutzername": "Nome utente",
"Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle.": "Qui trovi i documenti del/della trainer (soluzioni, presentazioni ecc.) per il tuo Circle.",
"Klicke auf den Button, um dich über SSO anzumelden oder zu registrieren.": "Clicca sul pulsante per effettuare il login o registrarti tramite SSO.",
"MS Teams öffnen": "Aprire MS Teams",
"Nächste Termine:": "Prossime scadenze:",
"Passwort": "Password",
"SSO Login/Registration": "Login/Registrazione SSO",
"Trainerunterlagen": "Documenti del/della trainer",
"Zur Zeit sind keine Termine vorhanden": "Al momento non ci sono scadenze",
"assignment": {
@ -68,7 +64,8 @@
"profileLink": "Mostrare il profilo",
"progress": "Partecipanti / Stato",
"tasksDone": "Incarichi di trasferimento completati dal/dalla partecipante.",
"title": "Cockpit"
"title": "Cockpit",
"trainerFilesText": "Qui trovi i documenti del/della trainer (soluzioni, presentazioni ecc.) per il tuo Circle."
},
"competences": {
"assessAgain": "Nuova auto-valutazione",
@ -91,6 +88,9 @@
"nocourses": "Non sei ancora stato/a assegnato/a a nessun corso.",
"welcome": "Ti diamo il benvenuto, {{name}}"
},
"dueDates": {
"nextDueDates": "Prossime scadenze"
},
"feedback": {
"answers": "Risposte",
"areYouSatisfied": "Quanto sei soddisfatto/a?",
@ -138,14 +138,18 @@
"backCapitalized": "@.capitalize:general.back",
"backToCircle": "Torna al Circle",
"backToLearningPath": "Torna al percorso formativo",
"certificate_many": "Certificati",
"certificate_one": "Certificato",
"certificate_other": "Certificati",
"circles": "Circle",
"close": "Chiudere",
"exam_one": "Esame",
"exam_other": "Esami",
"examResult_many": "Risultati degli esami",
"examResult_one": "Risultato dellesame",
"examResult_other": "Risultati degli esami",
"exam_many": "Esami",
"exam_one": "Esame",
"exam_other": "Esami",
"feedback_many": "Feedback",
"feedback_one": "Feedback",
"feedback_other": "Feedback",
"introduction": "Introduzione",
@ -155,6 +159,7 @@
"next": "Avanti",
"nextStep": "Continua",
"no": "No",
"notification_many": "Notifiche",
"notification_one": "Notifica",
"notification_other": "Notifiche",
"profileLink": "Mostrare i dettagli",
@ -167,6 +172,7 @@
"start": "Si comincia",
"submission": "Consegna",
"title": "myAFA",
"transferTask_many": "Incarichi di trasferimento",
"transferTask_one": "Incarico di trasferimento",
"transferTask_other": "Incarichi di trasferimento",
"yes": "Sì"
@ -189,6 +195,12 @@
"topics": "Temi:",
"welcomeBack": "Bentornato/a al tuo corso:"
},
"login": {
"demoLogin": "Login Demo",
"login": "Login",
"ssoLogin": "Login/Registrazione SSO",
"ssoText": "Clicca sul pulsante per effettuare il login o registrarti tramite SSO."
},
"mainNavigation": {
"logout": "Logout",
"profile": "Profilo"
@ -196,6 +208,7 @@
"mediaLibrary": {
"handlungsfelder": {
"description": "Trova tutte le risorse dei campi dazione, come materiali didattici, link e altre informazioni utili.",
"title_many": "Campi dazione",
"title_one": "Campo dazione",
"title_other": "Campi dazione"
},

View File

@ -25,8 +25,7 @@ const userStore = useUserStore();
<template>
<main class="bg-gray-200 lg:px-12 lg:py-12">
<div class="container-medium">
<h1 class="mb-8">Login</h1>
<h2>{{ $t("welcome") }}</h2>
<h1 class="mb-8">{{ $t("login.login") }}</h1>
<form
v-if="loginMethod === 'local'"
@ -74,20 +73,16 @@ const userStore = useUserStore();
<div v-if="loginMethod === 'sso'" class="bg-white p-4 lg:p-8">
<p>
{{
$t(
"Klicke auf den Button, um dich über SSO anzumelden oder zu registrieren."
)
}}
{{ $t("login.ssoText") }}
</p>
<p class="btn-primary mt-8">
<a :href="`/sso/login/?lang=${userStore.language}`">
{{ $t("SSO Login/Registration") }}
{{ $t("login.ssoLogin") }}
</a>
</p>
<p class="mt-8">
<a href="/login-local">
{{ $t("Demo Login") }}
{{ $t("login.demoLogin") }}
</a>
</p>
</div>

View File

@ -99,11 +99,7 @@ function setActiveClasses(translationKey: string) {
{{ $t("Trainerunterlagen") }}
</h3>
<div class="mb-4">
{{
$t(
"Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle."
)
}}
{{ $t("cockpit.trainerFilesText") }}
</div>
</div>
<div>

View File

@ -1,5 +1,5 @@
<template>
<p class="mb-4 font-bold">{{ $t("Nächste Termine:") }}</p>
<p class="mb-4 font-bold">{{ $t("dueDates.nextDueDates") }}:</p>
<!-- ul>
<li class="border-b border-t py-3">
<p class="pr-12">24. November 2022, 11 Uhr - Austausch mit Trainer</p>

Binary file not shown.

View File

@ -506,11 +506,12 @@ CSP_DEFAULT_SRC = [
"ws://127.0.0.1:5173",
"localhost:8000",
"localhost:8001",
"*.locize.app",
"blob:",
"data:",
"http://*",
]
CSP_FRAME_ANCESTORS = ("'self'",)
CSP_FRAME_ANCESTORS = ("'self'", "https://www.locize.app")
SPECTACULAR_SETTINGS = {
"TITLE": "VBV Lernwelt API",