latest dev + i18n

This commit is contained in:
_Bastler 2022-01-27 13:03:59 +01:00
parent e3b58a0d56
commit 7e987ad470
12 changed files with 70 additions and 34 deletions

View File

@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
import { HtmlUtils } from "../../WebRtc/HtmlUtils"; import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { i18nJson } from "../../i18n/locales";
function onClick(callback: () => void) { function onClick(callback: () => void) {
callback(); callback();
@ -8,20 +10,7 @@
function i18n(text: string | number | boolean | undefined): string { function i18n(text: string | number | boolean | undefined): string {
if (typeof text === "string") { if (typeof text === "string") {
if (text.trim().startsWith("{")) { return i18nJson(text);
try {
let textObject = JSON.parse(text);
if (textObject[$locale]) {
return textObject[$locale];
} else if (Object.keys(textObject).length > 0) {
// fallback to first value
return textObject[Object.keys(textObject)[0]];
}
} catch (err) {
//
}
}
return text;
} }
return ""; return "";
} }

View File

@ -34,5 +34,5 @@
<SoundMeterWidget {stream} /> <SoundMeterWidget {stream} />
{/if} {/if}
</div> </div>
<div class="is-silent" class:hide={isSilent}>{$LL.camera.my.silentZone()}</div> <div class="nes-container is-dark is-silent" class:hide={isSilent}>{$LL.camera.my.silentZone()}</div>
</div> </div>

View File

@ -17,6 +17,9 @@ import { isRegisterData } from "../Messages/JsonMessages/RegisterData";
import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData";
import { limitMapStore } from "../Stores/GameStore"; import { limitMapStore } from "../Stores/GameStore";
import { showLimitRoomModalStore } from "../Stores/ModalStore"; import { showLimitRoomModalStore } from "../Stores/ModalStore";
import { locales } from "../i18n/i18n-util";
import type { Locales } from "../i18n/i18n-types";
import { setCurrentLocale } from "../i18n/locales";
class ConnectionManager { class ConnectionManager {
private localUser!: LocalUser; private localUser!: LocalUser;
@ -343,9 +346,12 @@ class ConnectionManager {
throw new Error("No Auth code provided"); throw new Error("No Auth code provided");
} }
} }
const { authToken, userUuid, textures, email, username } = await Axios.get(`${PUSHER_URL}/login-callback`, { const { authToken, userUuid, textures, email, username, locale } = await Axios.get(
`${PUSHER_URL}/login-callback`,
{
params: { code, nonce, token, playUri: this.currentRoom?.key }, params: { code, nonce, token, playUri: this.currentRoom?.key },
}).then((res) => { }
).then((res) => {
return res.data; return res.data;
}); });
localUserStore.setAuthToken(authToken); localUserStore.setAuthToken(authToken);
@ -355,6 +361,25 @@ class ConnectionManager {
if (username) { if (username) {
gameManager.setPlayerName(username); gameManager.setPlayerName(username);
} }
if (locale) {
try {
if (locales.indexOf(locale) == -1) {
locales.forEach((l) => {
if (l.startsWith(locale.split("-")[0])) {
setCurrentLocale(l);
return;
}
});
} else {
setCurrentLocale(locale as Locales);
}
} catch (err) {
console.warn("Could not set locale", err);
}
} else {
console.log("no locale", locale);
}
//user connected, set connected store for menu at true //user connected, set connected store for menu at true
userIsConnected.set(true); userIsConnected.set(true);
} }

View File

@ -92,6 +92,7 @@ import { followUsersColorStore } from "../../Stores/FollowStore";
import Camera = Phaser.Cameras.Scene2D.Camera; import Camera = Phaser.Cameras.Scene2D.Camera;
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler"; import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
import { locale } from "../../i18n/i18n-svelte"; import { locale } from "../../i18n/i18n-svelte";
import { i18nJson } from "../../i18n/locales";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface | null; initPosition: PointInterface | null;
@ -1066,7 +1067,7 @@ export class GameScene extends DirtyScene {
); );
return; return;
} }
const escapedMessage = HtmlUtils.sanitize(openPopupEvent.message); const escapedMessage = HtmlUtils.sanitize(i18nJson(openPopupEvent.message));
let html = `<div id="container" hidden><div class="nes-container with-title is-centered">`; let html = `<div id="container" hidden><div class="nes-container with-title is-centered">`;
html += escapedMessage; html += escapedMessage;
if (openPopupEvent.input) { if (openPopupEvent.input) {
@ -1080,7 +1081,9 @@ export class GameScene extends DirtyScene {
for (const button of openPopupEvent.buttons) { for (const button of openPopupEvent.buttons) {
html += `<button type="button" class="nes-btn is-${HtmlUtils.escapeHtml( html += `<button type="button" class="nes-btn is-${HtmlUtils.escapeHtml(
button.className ?? "" button.className ?? ""
)}" id="popup-${openPopupEvent.popupId}-${id}">${HtmlUtils.escapeHtml(button.label)}</button>`; )}" id="popup-${openPopupEvent.popupId}-${id}">${HtmlUtils.escapeHtml(
i18nJson(button.label)
)}</button>`;
id++; id++;
} }
html += "</div>"; html += "</div>";

View File

@ -28,7 +28,7 @@ const de_DE: Translation = {
menu, menu,
report, report,
warning, warning,
emiji, emoji,
}; };
export default de_DE; export default de_DE;

View File

@ -114,6 +114,7 @@ const menu: NonNullable<Translation["menu"]> = {
}, },
sub: { sub: {
profile: "Profil", profile: "Profil",
worlds: "Welt",
settings: "Einstellungen", settings: "Einstellungen",
invite: "Einladung", invite: "Einladung",
credit: "Über die Karte", credit: "Über die Karte",

View File

@ -113,6 +113,7 @@ const menu: BaseTranslation = {
}, },
sub: { sub: {
profile: "Profile", profile: "Profile",
worlds: "Worlds",
settings: "Settings", settings: "Settings",
invite: "Invite", invite: "Invite",
credit: "Credit", credit: "Credit",

View File

@ -1,8 +1,9 @@
import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors"; import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors";
import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable"; import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable";
import { initI18n, setLocale } from "./i18n-svelte"; import { initI18n, setLocale, locale } from "./i18n-svelte";
import type { Locales, Translation } from "./i18n-types"; import type { Locales, Translation } from "./i18n-types";
import { baseLocale, getTranslationForLocale, locales } from "./i18n-util"; import { baseLocale, getTranslationForLocale, locales } from "./i18n-util";
import { get } from "svelte/store";
const fallbackLocale = FALLBACK_LOCALE || baseLocale; const fallbackLocale = FALLBACK_LOCALE || baseLocale;
const localStorageProperty = "language"; const localStorageProperty = "language";
@ -50,3 +51,20 @@ function getDisplayableLocales() {
} }
export const displayableLocales = getDisplayableLocales(); export const displayableLocales = getDisplayableLocales();
export const i18nJson = (text: string): string => {
if (text.trim().startsWith("{")) {
try {
const textObject = JSON.parse(text);
if (textObject[get(locale)]) {
return textObject[get(locale)];
} else if (Object.keys(textObject).length > 0) {
// fallback to first value
return textObject[Object.keys(textObject)[0]];
}
} catch (err) {
//
}
}
return text;
};

View File

@ -1047,15 +1047,12 @@ div.action.danger p.action-body{
} }
div.is-silent { div.is-silent {
position: absolute; position: absolute !important;
bottom: 40px; bottom: 40px;
border-radius: 15px 15px 15px 15px;
max-height: 20%; max-height: 20%;
transition: right 350ms; transition: right 350ms;
right: -300px; right: -1000px;
background-color: black;
font-size: 20px; font-size: 20px;
color: white;
padding: 30px 20px; padding: 30px 20px;
} }
div.is-silent.hide { div.is-silent.hide {

View File

@ -91,7 +91,7 @@ export class AuthenticateController extends BaseController {
const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken);
res.writeStatus("200"); res.writeStatus("200");
this.addCorsHeaders(res); this.addCorsHeaders(res);
return res.end(JSON.stringify({ ...resCheckTokenAuth, ...resUserData, username: authTokenData.username, authToken: token })); return res.end(JSON.stringify({ ...resCheckTokenAuth, ...resUserData, username: authTokenData.username, locale: authTokenData.locale, authToken: token }));
} catch (err) { } catch (err) {
console.info("User was not connected", err); console.info("User was not connected", err);
} }
@ -113,7 +113,7 @@ export class AuthenticateController extends BaseController {
if (!sub) { if (!sub) {
throw new Error("No sub in the response"); throw new Error("No sub in the response");
} }
const authToken = jwtTokenManager.createAuthToken(sub, userInfo?.access_token, userInfo?.username); const authToken = jwtTokenManager.createAuthToken(sub, userInfo?.access_token, userInfo?.username, userInfo.locale);
//Get user data from Admin Back Office //Get user data from Admin Back Office
//This is very important to create User Local in LocalStorage in WorkAdventure //This is very important to create User Local in LocalStorage in WorkAdventure
@ -121,7 +121,7 @@ export class AuthenticateController extends BaseController {
res.writeStatus("200"); res.writeStatus("200");
this.addCorsHeaders(res); this.addCorsHeaders(res);
return res.end(JSON.stringify({ ...data, authToken, username: userInfo.username, userUuid : sub })); return res.end(JSON.stringify({ ...data, authToken, username: userInfo.username, locale: userInfo.locale, userUuid : sub }));
} catch (e) { } catch (e) {
console.error("openIDCallback => ERROR", e); console.error("openIDCallback => ERROR", e);
return this.errorToResponse(e, res); return this.errorToResponse(e, res);

View File

@ -6,6 +6,7 @@ export interface AuthTokenData {
identifier: string; //will be a sub (id) if logged in or an uuid if anonymous identifier: string; //will be a sub (id) if logged in or an uuid if anonymous
accessToken?: string; accessToken?: string;
username?: string; username?: string;
locale?: string;
} }
export interface AdminSocketTokenData { export interface AdminSocketTokenData {
authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from. authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from.
@ -17,8 +18,8 @@ class JWTTokenManager {
return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData; return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData;
} }
public createAuthToken(identifier: string, accessToken?: string, username?: string) { public createAuthToken(identifier: string, accessToken?: string, username?: string, locale?: string) {
return Jwt.sign({ identifier, accessToken, username }, SECRET_KEY, { expiresIn: "30d" }); return Jwt.sign({ identifier, accessToken, username, locale }, SECRET_KEY, { expiresIn: "30d" });
} }
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData { public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {

View File

@ -26,7 +26,7 @@ class OpenIDClient {
public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) { public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) {
return this.initClient().then((client) => { return this.initClient().then((client) => {
return client.authorizationUrl({ return client.authorizationUrl({
scope: "openid email", scope: "openid profile email",
prompt: "login", prompt: "login",
state: state, state: state,
nonce: nonce, nonce: nonce,
@ -36,7 +36,7 @@ class OpenIDClient {
}); });
} }
public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string; username: string }> { public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string; username: string, locale: string }> {
return this.initClient().then((client) => { return this.initClient().then((client) => {
return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => { return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => {
return client.userinfo(tokenSet).then((res) => { return client.userinfo(tokenSet).then((res) => {
@ -46,6 +46,7 @@ class OpenIDClient {
sub: res.sub, sub: res.sub,
access_token: tokenSet.access_token as string, access_token: tokenSet.access_token as string,
username: (res.preferred_username || res.username || res.nickname || res.name || res.email) as string, username: (res.preferred_username || res.username || res.nickname || res.name || res.email) as string,
locale: res.locale as string,
}; };
}); });
}); });