diff --git a/Logic/State/UserRelatedState.ts b/Logic/State/UserRelatedState.ts index d2161e14e..7d0d959f9 100644 --- a/Logic/State/UserRelatedState.ts +++ b/Logic/State/UserRelatedState.ts @@ -1,15 +1,15 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" -import { OsmConnection } from "../Osm/OsmConnection" -import { MangroveIdentity } from "../Web/MangroveReviews" -import { Store, Stores, UIEventSource } from "../UIEventSource" +import {OsmConnection} from "../Osm/OsmConnection" +import {MangroveIdentity} from "../Web/MangroveReviews" +import {Store, Stores, UIEventSource} from "../UIEventSource" import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource" -import { FeatureSource } from "../FeatureSource/FeatureSource" -import { Feature } from "geojson" -import { Utils } from "../../Utils" +import {FeatureSource} from "../FeatureSource/FeatureSource" +import {Feature} from "geojson" +import {Utils} from "../../Utils" import translators from "../../assets/translators.json" import codeContributors from "../../assets/contributors.json" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" -import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" +import {LayerConfigJson} from "../../Models/ThemeConfig/Json/LayerConfigJson" import usersettings from "../../assets/generated/layers/usersettings.json" import Locale from "../../UI/i18n/Locale" import LinkToWeblate from "../../UI/Base/LinkToWeblate" @@ -92,7 +92,7 @@ export default class UserRelatedState { this.osmConnection.GetLongPreference("identity", "mangrove") ) - this.InitializeLanguage(availableLanguages) + this.language.addCallbackAndRunD((language) => Locale.language.setData(language)) this.installedUserThemes = this.InitInstalledUserThemes() @@ -178,28 +178,6 @@ export default class UserRelatedState { ) } } - - private InitializeLanguage(availableLanguages?: string[]) { - this.language.addCallbackAndRunD((language) => Locale.language.setData(language)) - Locale.language.addCallback((currentLanguage) => { - if (Locale.showLinkToWeblate.data) { - return true // Disable auto switching as we are in translators mode - } - if (availableLanguages?.indexOf(currentLanguage) < 0) { - console.log( - "Resetting language to", - availableLanguages[0], - "as", - currentLanguage, - " is unsupported" - ) - // The current language is not supported -> switch to a supported one - Locale.language.setData(availableLanguages[0]) - } - }) - Locale.language.ping() - } - private InitInstalledUserThemes(): Store { const prefix = "mapcomplete-unofficial-theme-" const postfix = "-combined-length" diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 3b2fe4439..32053243b 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -54,6 +54,7 @@ export class QueryParameters { } public static wasInitialized(key: string): boolean { + this.init() return QueryParameters._wasInitialized.has(key) } @@ -76,11 +77,10 @@ export class QueryParameters { if (window?.location?.search) { const params = window.location.search.substr(1).split("&") for (const param of params) { - const kv = param.split("=") - const key = decodeURIComponent(kv[0]) + const [key, value] = param.split("=") QueryParameters.addOrder(key) QueryParameters._wasInitialized.add(key) - const v = decodeURIComponent(kv[1]) + const v = decodeURIComponent(value) const source = new UIEventSource(v) source.addCallback(() => QueryParameters.Serialize()) QueryParameters.knownSources[key] = source @@ -131,4 +131,5 @@ export class QueryParameters { QueryParameters._wasInitialized.clear() QueryParameters.order = [] } + } diff --git a/UI/i18n/Locale.ts b/UI/i18n/Locale.ts index 9f2e6b0c3..b56950820 100644 --- a/UI/i18n/Locale.ts +++ b/UI/i18n/Locale.ts @@ -1,7 +1,7 @@ -import { UIEventSource } from "../../Logic/UIEventSource" -import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource" -import { Utils } from "../../Utils" -import { QueryParameters } from "../../Logic/Web/QueryParameters" +import {UIEventSource} from "../../Logic/UIEventSource" +import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource" +import {Utils} from "../../Utils" +import {QueryParameters} from "../../Logic/Web/QueryParameters" export default class Locale { public static showLinkToWeblate: UIEventSource = new UIEventSource(false) @@ -11,26 +11,50 @@ export default class Locale { public static showLinkOnMobile: UIEventSource = new UIEventSource(false) public static language: UIEventSource = Locale.setup() + /** + * Creates the UIEventSource containing the identifier of the current language + * + * If the QueryParameter 'language' is set, this query parameter will be used as backing source value + * If not set, a localStorageSource will be used. This will use the navigator language by default + * + * Note that other parts of the code (most notably the UserRelatedState) might sync language selection with OSM. + * + * + * @private + */ private static setup() { - let browserLanguage = "en" - if (typeof navigator !== "undefined") { - browserLanguage = navigator.languages?.[0] ?? navigator.language ?? "en" + + let source: UIEventSource + + if (QueryParameters.wasInitialized("language") || Utils.runningFromConsole) { + console.log("Language was initialized via URL-parameter - using the URL parameter as store instead of local storage", QueryParameters.wasInitialized("language")) + source = QueryParameters.GetQueryParameter( + "language", + undefined, + ["The language to display MapComplete in.", + "The user display language is determined in the following order:", + "- If the user did log in and did set their language before with MapComplete, use this language", + "- If the user visited MapComplete before and did change their language, use the language as set by this URL-parameter. This will _disable_ saving the language to localStorage in case a non-logged-in user changes their language", + "- Use the navigator-language (if available)", + "- Use English", + "", + "Translations are never complete. If a translation in a certain language is missing, English is used as fallback."].join("\n"), + ) + } else { + let browserLanguage = "en" + if (typeof navigator !== "undefined") { + browserLanguage = navigator.languages?.[0] ?? navigator.language ?? "en" + console.log("Browser language is", browserLanguage) + } + source = LocalStorageSource.Get("language", browserLanguage) } - const source = LocalStorageSource.Get("language", browserLanguage) + if (!Utils.runningFromConsole) { // @ts-ignore window.setLanguage = function (language: string) { source.setData(language) } } - if (QueryParameters.wasInitialized("language")) { - const qp = QueryParameters.GetQueryParameter( - "language", - undefined, - "The language to display mapcomplete in. Will be ignored in case a logged-in-user did set their language before. If the specified language does not exist, it will default to the first language in the theme." - ) - Locale.language.setData(qp.data) - } QueryParameters.GetBooleanQueryParameter( "fs-translation-mode",