mirror of
https://github.com/ClaperCo/Claper.git
synced 2025-12-16 11:57:58 +01:00
feat: add hu and lv locales to airpicker and moment
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Add `LANGUAGES` setting to configure available languages in the app
|
- Add `LANGUAGES` setting to configure available languages in the app
|
||||||
|
- Add Latvian language support (@possible-im)
|
||||||
|
- Add Hungarian language support (@bpisch)
|
||||||
|
|
||||||
### Fixes and improvements
|
### Fixes and improvements
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Claper has a two-sided mission:
|
|||||||
- The first one is to help these people presenting an idea or a message by giving them the opportunity to make their presentation unique and to have real-time feedback from their audience.
|
- The first one is to help these people presenting an idea or a message by giving them the opportunity to make their presentation unique and to have real-time feedback from their audience.
|
||||||
- The second one is to help each participant to take their place, to be an actor in the presentation, in the meeting and to feel important and useful.
|
- The second one is to help each participant to take their place, to be an actor in the presentation, in the meeting and to feel important and useful.
|
||||||
|
|
||||||
Supported languages: 🇬🇧 English, 🇫🇷 French, 🇩🇪 German, 🇪🇸 Spanish, 🇳🇱 Dutch, 🇮🇹 Italian
|
Supported languages: 🇬🇧 English, 🇫🇷 French, 🇩🇪 German, 🇪🇸 Spanish, 🇳🇱 Dutch, 🇮🇹 Italian, 🇭🇺 Hungarian, 🇱🇻 Latvian
|
||||||
|
|
||||||
### Built With
|
### Built With
|
||||||
|
|
||||||
@@ -101,6 +101,3 @@ Distributed under the GPLv3 License. See `LICENSE.txt` for more information.
|
|||||||
[Tailwind-url]: https://tailwindcss.com/
|
[Tailwind-url]: https://tailwindcss.com/
|
||||||
[Phoenix]: https://img.shields.io/badge/phoenix-f35424?style=for-the-badge&logo=&logoColor=white
|
[Phoenix]: https://img.shields.io/badge/phoenix-f35424?style=for-the-badge&logo=&logoColor=white
|
||||||
[Phoenix-url]: https://www.phoenixframework.org/
|
[Phoenix-url]: https://www.phoenixframework.org/
|
||||||
[lmddc-logo]: /priv/static/images/partners/lmddc.png
|
|
||||||
[pixilearn-logo]: /priv/static/images/partners/pixilearn.png
|
|
||||||
[uccs-logo]: /priv/static/images/partners/uccs.png
|
|
||||||
|
|||||||
@@ -13,11 +13,14 @@ import airdatepickerLocaleDe from "air-datepicker/locale/de";
|
|||||||
import airdatepickerLocaleEs from "air-datepicker/locale/es";
|
import airdatepickerLocaleEs from "air-datepicker/locale/es";
|
||||||
import airdatepickerLocaleNl from "air-datepicker/locale/nl";
|
import airdatepickerLocaleNl from "air-datepicker/locale/nl";
|
||||||
import airdatepickerLocaleIt from "air-datepicker/locale/it";
|
import airdatepickerLocaleIt from "air-datepicker/locale/it";
|
||||||
|
import airdatepickerLocaleHu from "air-datepicker/locale/hu";
|
||||||
import "moment/locale/de";
|
import "moment/locale/de";
|
||||||
import "moment/locale/fr";
|
import "moment/locale/fr";
|
||||||
import "moment/locale/es";
|
import "moment/locale/es";
|
||||||
import "moment/locale/nl";
|
import "moment/locale/nl";
|
||||||
import "moment/locale/it";
|
import "moment/locale/it";
|
||||||
|
import "moment/locale/hu";
|
||||||
|
import "moment/locale/lv";
|
||||||
import QRCodeStyling from "qr-code-styling";
|
import QRCodeStyling from "qr-code-styling";
|
||||||
import { Presenter } from "./presenter";
|
import { Presenter } from "./presenter";
|
||||||
import { Manager } from "./manager";
|
import { Manager } from "./manager";
|
||||||
@@ -26,28 +29,53 @@ import { TourGuideClient } from "@sjmc11/tourguidejs/src/Tour";
|
|||||||
window.moment = moment;
|
window.moment = moment;
|
||||||
|
|
||||||
// Get supported locales from backend configuration or fallback to default list
|
// Get supported locales from backend configuration or fallback to default list
|
||||||
const supportedLocales = window.claperConfig?.supportedLocales || ["en", "fr", "de", "es", "nl", "it"];
|
const supportedLocales = window.claperConfig?.supportedLocales || [
|
||||||
|
"en",
|
||||||
|
"fr",
|
||||||
|
"de",
|
||||||
|
"es",
|
||||||
|
"nl",
|
||||||
|
"it",
|
||||||
|
"hu",
|
||||||
|
"lv",
|
||||||
|
];
|
||||||
|
|
||||||
|
const airdatePickrSupportedLocales = window.claperConfig?.supportedLocales || [
|
||||||
|
"en",
|
||||||
|
"fr",
|
||||||
|
"de",
|
||||||
|
"es",
|
||||||
|
"nl",
|
||||||
|
"it",
|
||||||
|
"hu",
|
||||||
|
];
|
||||||
|
|
||||||
var locale =
|
var locale =
|
||||||
document.querySelector("html").getAttribute("lang") ||
|
document.querySelector("html").getAttribute("lang") ||
|
||||||
navigator.language.split("-")[0];
|
navigator.language.split("-")[0];
|
||||||
|
|
||||||
|
var airdatepickrLocale = locale;
|
||||||
|
|
||||||
if (!supportedLocales.includes(locale)) {
|
if (!supportedLocales.includes(locale)) {
|
||||||
locale = "en";
|
locale = "en";
|
||||||
}
|
}
|
||||||
|
if (!airdatePickrSupportedLocales.includes(locale)) {
|
||||||
|
airdatepickrLocale = "en";
|
||||||
|
}
|
||||||
|
|
||||||
window.moment.locale("en");
|
window.moment.locale("en");
|
||||||
window.moment.locale(locale);
|
window.moment.locale(locale);
|
||||||
window.Alpine = Alpine;
|
window.Alpine = Alpine;
|
||||||
Alpine.start();
|
Alpine.start();
|
||||||
|
|
||||||
let airdatepickerLocale = {
|
let airdatePickrLocales = {
|
||||||
en: airdatepickerLocaleEn,
|
en: airdatepickerLocaleEn,
|
||||||
fr: airdatepickerLocaleFr,
|
fr: airdatepickerLocaleFr,
|
||||||
de: airdatepickerLocaleDe,
|
de: airdatepickerLocaleDe,
|
||||||
es: airdatepickerLocaleEs,
|
es: airdatepickerLocaleEs,
|
||||||
nl: airdatepickerLocaleNl,
|
nl: airdatepickerLocaleNl,
|
||||||
it: airdatepickerLocaleIt,
|
it: airdatepickerLocaleIt,
|
||||||
|
hu: airdatepickerLocaleHu,
|
||||||
};
|
};
|
||||||
let csrfToken = document
|
let csrfToken = document
|
||||||
.querySelector("meta[name='csrf-token']")
|
.querySelector("meta[name='csrf-token']")
|
||||||
@@ -70,8 +98,8 @@ Hooks.EmbeddedBanner = {
|
|||||||
Hooks.TourGuide = {
|
Hooks.TourGuide = {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.triggerDiv = document.querySelector(this.el.dataset.btnTrigger);
|
this.triggerDiv = document.querySelector(this.el.dataset.btnTrigger);
|
||||||
this.btnTrigger = this.triggerDiv.querySelector('.open');
|
this.btnTrigger = this.triggerDiv.querySelector(".open");
|
||||||
this.closeBtnTrigger = this.triggerDiv.querySelector('.close');
|
this.closeBtnTrigger = this.triggerDiv.querySelector(".close");
|
||||||
|
|
||||||
this.tour = new TourGuideClient({
|
this.tour = new TourGuideClient({
|
||||||
nextLabel: this.el.dataset.nextLabel,
|
nextLabel: this.el.dataset.nextLabel,
|
||||||
@@ -106,7 +134,7 @@ Hooks.TourGuide = {
|
|||||||
destroyed() {
|
destroyed() {
|
||||||
this.btnTrigger.removeEventListener("click", () => {
|
this.btnTrigger.removeEventListener("click", () => {
|
||||||
this.startTour();
|
this.startTour();
|
||||||
});
|
});
|
||||||
this.closeBtnTrigger.removeEventListener("click", () => {
|
this.closeBtnTrigger.removeEventListener("click", () => {
|
||||||
this.triggerDiv.classList.add("hidden");
|
this.triggerDiv.classList.add("hidden");
|
||||||
this.tour.finishTour(true, this.el.dataset.group);
|
this.tour.finishTour(true, this.el.dataset.group);
|
||||||
@@ -201,26 +229,36 @@ Hooks.Scroll = {
|
|||||||
Hooks.ScrollIntoDiv = {
|
Hooks.ScrollIntoDiv = {
|
||||||
mounted() {
|
mounted() {
|
||||||
let useParent = this.el.dataset.useParent === "true";
|
let useParent = this.el.dataset.useParent === "true";
|
||||||
this.scrollElement = this.el.dataset.useParent === "true" ? this.el.parentElement : this.el;
|
this.scrollElement =
|
||||||
|
this.el.dataset.useParent === "true" ? this.el.parentElement : this.el;
|
||||||
this.checkIfAtBottom();
|
this.checkIfAtBottom();
|
||||||
this.scrollToBottom(true);
|
this.scrollToBottom(true);
|
||||||
this.handleEvent("scroll", () => this.scrollToBottom());
|
this.handleEvent("scroll", () => this.scrollToBottom());
|
||||||
this.scrollElement.addEventListener("scroll", () => this.checkIfAtBottom());
|
this.scrollElement.addEventListener("scroll", () => this.checkIfAtBottom());
|
||||||
},
|
},
|
||||||
checkIfAtBottom() {
|
checkIfAtBottom() {
|
||||||
this.isAtBottom = this.scrollElement.scrollHeight - this.scrollElement.scrollTop - this.scrollElement.clientHeight <= 30;
|
this.isAtBottom =
|
||||||
|
this.scrollElement.scrollHeight -
|
||||||
|
this.scrollElement.scrollTop -
|
||||||
|
this.scrollElement.clientHeight <=
|
||||||
|
30;
|
||||||
},
|
},
|
||||||
scrollToBottom(force = false) {
|
scrollToBottom(force = false) {
|
||||||
if (force || this.isAtBottom) {
|
if (force || this.isAtBottom) {
|
||||||
this.scrollElement.scrollTo({ top: this.scrollElement.scrollHeight, behavior: "smooth" });
|
this.scrollElement.scrollTo({
|
||||||
|
top: this.scrollElement.scrollHeight,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.scrollElement.removeEventListener("scroll", () => this.checkIfAtBottom());
|
this.scrollElement.removeEventListener("scroll", () =>
|
||||||
}
|
this.checkIfAtBottom(),
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Hooks.NicknamePicker = {
|
Hooks.NicknamePicker = {
|
||||||
@@ -244,7 +282,7 @@ Hooks.NicknamePicker = {
|
|||||||
clicked(e) {
|
clicked(e) {
|
||||||
let nickname = prompt(
|
let nickname = prompt(
|
||||||
this.el.dataset.prompt,
|
this.el.dataset.prompt,
|
||||||
localStorage.getItem("nickname") || ""
|
localStorage.getItem("nickname") || "",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (nickname) {
|
if (nickname) {
|
||||||
@@ -354,7 +392,7 @@ Hooks.Pickr = {
|
|||||||
const utc = moment(date).utc().format("YYYY-MM-DDTHH:mm:ss");
|
const utc = moment(date).utc().format("YYYY-MM-DDTHH:mm:ss");
|
||||||
utcTime.value = utc;
|
utcTime.value = utc;
|
||||||
},
|
},
|
||||||
locale: airdatepickerLocale[locale],
|
locale: airdatePickrLocales[airdatepickrLocale],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updated() {},
|
updated() {},
|
||||||
@@ -393,7 +431,7 @@ Hooks.OpenPresenter = {
|
|||||||
window.open(
|
window.open(
|
||||||
this.el.dataset.url,
|
this.el.dataset.url,
|
||||||
"newwindow",
|
"newwindow",
|
||||||
"width=" + window.screen.width + ",height=" + window.screen.height
|
"width=" + window.screen.width + ",height=" + window.screen.height,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -418,7 +456,12 @@ Hooks.GlobalReacts = {
|
|||||||
const container = document.createElement("div");
|
const container = document.createElement("div");
|
||||||
container.innerHTML = svgContent;
|
container.innerHTML = svgContent;
|
||||||
const svgElement = container.firstChild;
|
const svgElement = container.firstChild;
|
||||||
svgElement.classList.add("react-animation", "absolute", "transform", "opacity-0");
|
svgElement.classList.add(
|
||||||
|
"react-animation",
|
||||||
|
"absolute",
|
||||||
|
"transform",
|
||||||
|
"opacity-0",
|
||||||
|
);
|
||||||
svgElement.classList.add(...this.el.className.split(" "));
|
svgElement.classList.add(...this.el.className.split(" "));
|
||||||
this.el.appendChild(svgElement);
|
this.el.appendChild(svgElement);
|
||||||
}
|
}
|
||||||
@@ -430,15 +473,17 @@ Hooks.GlobalReacts = {
|
|||||||
|
|
||||||
preloadSVGs() {
|
preloadSVGs() {
|
||||||
const svgTypes = ["heart", "hundred", "clap", "raisehand"];
|
const svgTypes = ["heart", "hundred", "clap", "raisehand"];
|
||||||
svgTypes.forEach(type => {
|
svgTypes.forEach((type) => {
|
||||||
fetch(`/images/icons/${type}.svg`)
|
fetch(`/images/icons/${type}.svg`)
|
||||||
.then(response => response.text())
|
.then((response) => response.text())
|
||||||
.then(svgContent => {
|
.then((svgContent) => {
|
||||||
this.svgCache[type] = svgContent;
|
this.svgCache[type] = svgContent;
|
||||||
})
|
})
|
||||||
.catch(error => console.error(`Error loading SVG for ${type}:`, error));
|
.catch((error) =>
|
||||||
|
console.error(`Error loading SVG for ${type}:`, error),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
Hooks.JoinEvent = {
|
Hooks.JoinEvent = {
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -630,7 +675,6 @@ window.addEventListener("phx:page-loading-stop", (info) => {
|
|||||||
topbar.hide();
|
topbar.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const onlineUserTemplate = function (user) {
|
const onlineUserTemplate = function (user) {
|
||||||
return `
|
return `
|
||||||
<div id="online-user">
|
<div id="online-user">
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ allow_unlink_external_provider =
|
|||||||
logout_redirect_url = get_var_from_path_or_env(config_dir, "LOGOUT_REDIRECT_URL", nil)
|
logout_redirect_url = get_var_from_path_or_env(config_dir, "LOGOUT_REDIRECT_URL", nil)
|
||||||
|
|
||||||
languages =
|
languages =
|
||||||
get_var_from_path_or_env(config_dir, "LANGUAGES", "en,fr,es")
|
get_var_from_path_or_env(config_dir, "LANGUAGES", "en,fr,es,it,de")
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|> Enum.map(&String.trim/1)
|
|> Enum.map(&String.trim/1)
|
||||||
|
|
||||||
|
|||||||
@@ -259,11 +259,19 @@
|
|||||||
{"English", "en"},
|
{"English", "en"},
|
||||||
{"Español", "es"},
|
{"Español", "es"},
|
||||||
{"Français", "fr"},
|
{"Français", "fr"},
|
||||||
|
{"Hungarian", "hu"},
|
||||||
{"Italiano", "it"},
|
{"Italiano", "it"},
|
||||||
|
{"Latvian", "lv"},
|
||||||
{"Nederlands", "nl"}
|
{"Nederlands", "nl"}
|
||||||
]
|
]
|
||||||
|> Enum.filter(fn {_name, code} ->
|
|> Enum.filter(fn {_name, code} ->
|
||||||
code in Application.get_env(:claper, :languages, ["en", "fr", "es"])
|
code in Application.get_env(:claper, :languages, [
|
||||||
|
"en",
|
||||||
|
"fr",
|
||||||
|
"es",
|
||||||
|
"it",
|
||||||
|
"de"
|
||||||
|
])
|
||||||
end)
|
end)
|
||||||
}
|
}
|
||||||
key={:locale}
|
key={:locale}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<link phx-track-static rel="stylesheet" href="/assets/custom.css" />
|
<link phx-track-static rel="stylesheet" href="/assets/custom.css" />
|
||||||
<script>
|
<script>
|
||||||
window.claperConfig = {
|
window.claperConfig = {
|
||||||
supportedLocales: <%= Jason.encode!(Application.get_env(:claper, :languages, ["en", "fr", "es"])) %>
|
supportedLocales: <%= Jason.encode!(Application.get_env(:claper, :languages, ["en", "fr", "es", "it", "de"])) %>
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script defer phx-track-static type="text/javascript" src="/assets/app.js">
|
<script defer phx-track-static type="text/javascript" src="/assets/app.js">
|
||||||
|
|||||||
Reference in New Issue
Block a user