i18n: make the app translatable (#196)

This commit is contained in:
Tyler Wilding 2023-05-06 12:10:54 -05:00 committed by GitHub
parent 771286dd82
commit 8d7fe7c03f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 838 additions and 427 deletions

3
crowdin.yml Normal file
View file

@ -0,0 +1,3 @@
files:
- source: /src/assets/translations/en-US.json
translation: /src/assets/translations/%locale%.json

View file

@ -40,6 +40,7 @@
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.0",
"svelte": "^3.58.0",
"svelte-i18n": "^3.6.0",
"svelte-preprocess": "^5.0.3",
"tailwindcss": "^3.3.2",
"typescript": "^5.0.4",
@ -47,6 +48,7 @@
},
"dependencies": {
"@tauri-apps/api": "^1.2.0",
"country-flag-emoji-polyfill": "^0.1.4",
"svelte-navigator": "^3.2.2"
}
}

View file

@ -201,3 +201,23 @@ pub async fn get_active_tooling_version_folder(
let config_lock = config.lock().await;
Ok(config_lock.active_version_folder.clone())
}
#[tauri::command]
pub async fn get_locale(
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
) -> Result<Option<String>, CommandError> {
let config_lock = config.lock().await;
Ok(config_lock.locale.clone())
}
#[tauri::command]
pub async fn set_locale(
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
locale: String,
) -> Result<(), CommandError> {
let mut config_lock = config.lock().await;
config_lock
.set_locale(locale)
.map_err(|_| CommandError::Configuration(format!("Unable to persist locale change")))?;
Ok(())
}

View file

@ -117,6 +117,7 @@ pub struct LauncherConfig {
pub installation_dir: Option<String>,
pub active_version: Option<String>,
pub active_version_folder: Option<String>,
pub locale: Option<String>,
}
fn default_version() -> Option<String> {
@ -134,6 +135,7 @@ impl LauncherConfig {
installation_dir: None,
active_version: None,
active_version_folder: Some("official".to_string()),
locale: None,
}
}
@ -271,6 +273,12 @@ impl LauncherConfig {
Ok(())
}
pub fn set_locale(&mut self, new_locale: String) -> Result<(), ConfigError> {
self.locale = Some(new_locale);
self.save_config()?;
Ok(())
}
// TODO - this pattern isn't great. It's made awkward by trying to be backwards compatible
// with the old format though
//

View file

@ -155,6 +155,8 @@ fn main() {
commands::config::set_opengl_requirement_met,
commands::config::save_active_version_change,
commands::config::set_install_directory,
commands::config::get_locale,
commands::config::set_locale,
commands::game::reset_game_settings,
commands::game::uninstall_game,
commands::support::generate_support_package,

View file

@ -6,7 +6,6 @@
import Settings from "./routes/Settings.svelte";
import Sidebar from "./components/sidebar/Sidebar.svelte";
import Background from "./components/background/Background.svelte";
import { appWindow } from "@tauri-apps/api/window";
import Header from "./components/header/Header.svelte";
import Textures from "./routes/Textures.svelte";
import Update from "./routes/Update.svelte";
@ -15,6 +14,7 @@
import { Toast } from "flowbite-svelte";
import Help from "./routes/Help.svelte";
import { toastStore } from "$lib/stores/ToastStore";
import { isLoading } from "svelte-i18n";
let revokeSpecificActions = false;
@ -59,41 +59,48 @@
</script>
<Router>
<div class="container h-screen max-w-none flex flex-col">
<Background />
<Header />
<div class="flex h-full">
<Sidebar />
<div id="content" class="basis-9/10">
<Route path="/" component={Game} primary={false} let:params />
<Route path="/:game_name" component={Game} primary={false} let:params />
<Route
path="/jak2"
component={GameInProgress}
primary={false}
let:params
/>
<Route
path="/settings/:tab"
component={Settings}
primary={false}
let:params
/>
<Route path="/faq" component={Help} primary={false} />
<Route path="/textures" component={Textures} primary={false} />
<Route path="/update" component={Update} primary={false} />
<div class={`container h-screen max-w-none flex flex-col bg-black`}>
{#if !$isLoading}
<Background />
<Header />
<div class="flex h-full z-10">
<Sidebar />
<div id="content" class="basis-9/10">
<Route path="/" component={Game} primary={false} let:params />
<Route
path="/:game_name"
component={Game}
primary={false}
let:params
/>
<Route
path="/jak2"
component={GameInProgress}
primary={false}
let:params
/>
<Route
path="/settings/:tab"
component={Settings}
primary={false}
let:params
/>
<Route path="/faq" component={Help} primary={false} />
<Route path="/textures" component={Textures} primary={false} />
<Route path="/update" component={Update} primary={false} />
</div>
</div>
</div>
{#if $toastStore.msg !== undefined}
<!-- TODO - make these look nice for info/warn/error levels -->
<Toast
color="green"
position="top-right"
class="top-20"
divClass="w-full max-w-xs p-2 pl-4"
>
{$toastStore.msg}
</Toast>
{#if $toastStore.msg !== undefined}
<!-- TODO - make these look nice for info/warn/error levels -->
<Toast
color="green"
position="top-right"
class="top-20"
divClass="w-full max-w-xs p-2 pl-4"
>
{$toastStore.msg}
</Toast>
{/if}
{/if}
</div>
</Router>

View file

@ -0,0 +1,118 @@
{
"header_updateAvailable": "Update Available!",
"setup_extractAndVerify": "Extract and Verify",
"setup_copyFiles": "Copy Files",
"setup_decompile": "Decompile",
"setup_compile": "Compile",
"setup_done": "Done",
"setup_button_continue": "Continue",
"setup_installationFailed": "Installation has Failed!",
"setup_button_getSupportPackage": "Get Support Package",
"setup_button_installViaISO": "Install via ISO",
"setup_logs_header": "Logs",
"setup_logs_truncation": "Last 250 Lines",
"gameUpdate_versionMismatch_title": "Version Mismatch Detected!",
"gameUpdate_versionMismatch_currentlyInstalled": "The game is already installed with",
"gameUpdate_versionMismatch_version": "Version",
"gameUpdate_versionMismatch_type": "Type",
"gameUpdate_versionMismatch_currentlySelected": "but you currently have selected",
"gameUpdate_versionMismatch_nextSteps": "You can either update the game to this new version (no save data will be lost) or you can rollback your active version to match",
"gameUpdate_versionMismatch_button_updateGame": "Update Game",
"gameUpdate_versionMismatch_button_changeVersion": "Change Version",
"requirements_notMet_header": "Unfortunately, your system does not meet all the minimum requirements or we were unable to check them",
"requirements_cpu_supportsAVX": "Your CPU supports AVX",
"requirements_cpu_unableToCheckAVX": "Unable to verify if your CPU supports AVX",
"requirements_cpu_doesNotSupportAVX": "Your CPU does not support AVX",
"requirements_cpu_avxExplanation_1": "This cannot be fixed without upgrading to a newer CPU",
"requirements_cpu_avxExplanation_2": "AVX support has been fairly standard since 2011",
"requirements_cpu_avxExplanation_3": "Click here for more information",
"requirements_gpu_supportsOpenGL": "Your GPU supports OpenGL 4.3",
"requirements_gpu_unableToCheckOpenGL": "Unable to verify if your GPU supports OpenGL 4.3",
"requirements_gpu_doesNotSupportOpenGL": "Your GPU does not support OpenGL 4.3",
"requirements_gpu_avxExplanation_1_preLink": "Lookup your GPU",
"requirements_gpu_avxExplanation_1_link": "here",
"requirements_gpu_avxExplanation_1_postLink": "to see if it should be supported",
"requirements_gpu_avxExplanation_2": "You can attempt to upgrade your GPU drivers",
"requirements_gpu_avxExplanation_3": "Otherwise, you will need to upgrade your GPU, most GPUs since 2012 support it",
"gameControls_button_play": "Play",
"gameControls_button_advanced": "Advanced",
"gameControls_button_playInDebug": "Play in Debug Mode",
"gameControls_button_openREPL": "Open REPL",
"gameControls_button_decompile": "Decompile",
"gameControls_button_decompile_helpText": "Extracts game assets (ie. to apply texture replacements)",
"gameControls_button_compile": "Compile",
"gameControls_button_compile_helpText": "Rebuild the game. (ie. after modifying OpenGOAL source code)",
"gameControls_button_openSettingsFolder": "Open Settings Folder",
"gameControls_button_openSavesFolder": "Open Saves Folder",
"gameControls_button_resetSettings": "Reset Settings",
"gameControls_button_uninstall_confirmation": "Are you sure you want to uninstall?",
"gameControls_button_uninstall": "Uninstall",
"gameControls_button_uninstall_helpText": "This will not delete any saves or settings",
"gameName_jak1": "Jak & Daxter: The Precursor Legacy",
"gameName_jak2": "Jak II",
"temp_jak2_indev_header": "Jak II is Currently in Development",
"temp_jak2_indev_subheader": "In the meantime, check out our latest progress reports showcasing what we've accomplished so far",
"temp_jak2_indev_progressReports": "Progress Reports",
"gameControls_noToolingSet_header": "No Tooling Version Configured!",
"gameControls_noToolingSet_subheader": "Head over to the following settings page to download the latest release",
"gameControls_noToolingSet_button_setVersion": "Set Version",
"sidebar_settings": "Settings",
"sidebar_help": "Help",
"settings_general_localeChange": "Change Locale",
"settings_general_localeChange_helper_1": "Consider ",
"settings_general_localeChange_helper_link": "contributing translations",
"settings_general_localeChange_helper_2": "if you can!",
"settings_general_button_resetSettings": "Reset Launcher Settings",
"settings_general_button_resetSettings_confirmation": "Are you sure you want to reset your launcher settings? This will reset your installation directory and any other settings you have changed. This will not affect your game files.",
"settings_folders_installationDir": "Installation Directory",
"settings_folders_installationDir_prompt": "Pick an Installation Folder",
"settings_versions_devel_tabName": "Development",
"settings_versions_devel_description": "This list serves as a convenient area to stage, manage and test new releases (either official or unofficial) This list will always require manual management via it's respective folder",
"settings_versions_official_tabName": "Official",
"settings_versions_official_description": "Official versions are from the `jak-project` GitHub repository",
"settings_versions_unofficial_tabName": "Unofficial",
"settings_versions_unofficial_description": "Unofficial versions are typically modified `jak-project` releases to enable changes or new content. These are not supported by the OpenGOAL team and will have to be manually added to the folder at this time",
"settings_versions_icon_save_altText": "save version change",
"settings_versions_icon_refresh_altText": "refresh version list",
"settings_versions_icon_openFolder_altText": "open version folder",
"settings_versions_icon_removeVersion_altText": "remove version",
"settings_versions_icon_downloadVersion_altText": "download version",
"settings_versions_icon_githubRelease_altText": "github release notes",
"settings_versions_table_header_version": "Version",
"settings_versions_table_header_date": "Date",
"settings_versions_table_header_changes": "Changes",
"settings_versions_header": "Configure your active tooling version",
"help_header": "Support & FAQ",
"help_foreword": "If you are reporting an issue or asking for help, download the following support package and attach it in your Discord thread or GitHub issue.",
"help_button_downloadPackage": "Download Support Package",
"help_button_openLogFolder": "Open Log Folder",
"help_description_createAnIssue": "You can either ask a question on our Discord, or create a GitHub issue with as much detail as possible.",
"help_description_duplicateReminder": "In either location, please do a quick search to see if the question has already been answered before",
"help_button_reportLauncherIssue": "Report Launcher Issue",
"help_button_reportGameIssue": "Report Game Issue",
"settings_tabs_general": "General",
"settings_tabs_folders": "General",
"settings_tabs_versions": "General",
"update_header": "Launcher Update Available",
"update_versionLabel": "Version",
"update_description": "View the changes below and click the button to update to the latest version. The launcher will restart when finished.",
"update_button_doUpdate": "Update Launcher",
"update_button_viewChangelog": "View Changelog",
"update_button_hideDependencyChanges": "Dependency Changes",
"update_changelog_header_contributor": "Contributor",
"update_changelog_header_description": "Description",
"update_changelog_header_pullRequest": "Pull Request",
"update_alreadyUpToDate": "You're Up to Date!",
"splash_selectLocale": "Select Locale",
"splash_deleteOldInstallDir": "The old installation folder is no longer needed, delete it?",
"splash_button_deleteOldInstallDir_yes": "Yes",
"splash_button_deleteOldInstallDir_no": "No",
"splash_noInstallDirSet": "No installation folder set!",
"splash_button_setInstallFolder": "Set Install Folder",
"splash_button_setInstallFolder_prompt": "Pick an Installation Folder",
"splash_step_readingSettings": "Reading Settings",
"splash_step_checkingDirectories": "Checking Directories",
"splash_step_pickInstallFolder": "Pick an Installation Folder",
"splash_step_finishingUp": "Finishing Up",
"splash_step_errorOpening": "Problem opening Launcher"
}

View file

@ -1,3 +0,0 @@
{
"jak1_gameName": "Jak & Daxter: The Precursor Legacy"
}

View file

@ -11,7 +11,7 @@
const location = useLocation();
$: $location.pathname, updateStyle();
let style = "absolute -z-50 object-fill h-screen";
let style = "absolute object-fill h-screen";
onMount(async () => {
const unlistenInstalled = await listen("gameInstalled", (event) => {
@ -23,7 +23,7 @@
});
async function updateStyle(): Promise<void> {
let newStyle = "absolute -z-50 object-fill h-screen";
let newStyle = "absolute object-fill h-screen";
let pathname = $location.pathname;
if (pathname === "/jak1" || pathname === "/") {
if (!(await isGameInstalled("jak1"))) {

View file

@ -0,0 +1,9 @@
<script lang="ts">
import logo from "$assets/images/icon.webp";
import { Spinner } from "flowbite-svelte";
</script>
<div class="h-screen flex flex-col items-center justify-center bg-black">
<img data-tauri-drag-region src={logo} alt="" srcset="" class="w-48" />
<Spinner color="yellow" size={"12"} class="mt-5" />
</div>

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { getGameTitle, getInternalName, SupportedGame } from "$lib/constants";
import { getInternalName, SupportedGame } from "$lib/constants";
import { openDir } from "$lib/rpc/window";
import Icon from "@iconify/svelte";
import { configDir, join } from "@tauri-apps/api/path";
@ -15,6 +15,7 @@
import { resetGameSettings, uninstallGame } from "$lib/rpc/game";
import { platform } from "@tauri-apps/api/os";
import { launchGame, openREPL } from "$lib/rpc/binaries";
import { _ } from "svelte-i18n";
export let activeGame: SupportedGame;
@ -45,14 +46,14 @@
<h1
class="tracking-tighter text-2xl font-bold pb-3 text-orange-500 text-outline pointer-events-none"
>
{getGameTitle(activeGame)}
{$_(`gameName_${getInternalName(activeGame)}`)}
</h1>
<div class="flex flex-row gap-2">
<Button
btnClass="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => {
launchGame(getInternalName(activeGame), false);
}}>Play</Button
}}>{$_("gameControls_button_play")}</Button
>
<!-- TODO - texture replacements left out for now, get everything else working end-to-end first -->
<!-- <Button
@ -65,19 +66,19 @@
<Button
btnClass="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
>
Advanced
{$_("gameControls_button_advanced")}
</Button>
<Dropdown placement="top-end" frameClass="!bg-slate-900">
<DropdownItem
on:click={async () => {
launchGame(getInternalName(activeGame), true);
}}>Play&nbsp;in&nbsp;Debug&nbsp;Mode</DropdownItem
}}>{$_("gameControls_button_playInDebug")}</DropdownItem
>
{#if !isLinux}
<DropdownItem
on:click={async () => {
openREPL(getInternalName(activeGame));
}}>Open REPL</DropdownItem
}}>{$_("gameControls_button_openREPL")}</DropdownItem
>
{/if}
<DropdownDivider />
@ -87,10 +88,10 @@
type: "decompile",
});
}}
>Decompile
>{$_("gameControls_button_decompile")}
<!-- NOTE - this is a bug in flowbite-svelte, it's not replacing the default class but just appending -->
<Helper helperClass="!text-neutral-400 !text-xs"
>Extracts game assets (ie. to apply texture replacements)</Helper
>{$_("gameControls_button_decompile_helpText")}</Helper
></DropdownItem
>
<DropdownItem
@ -99,10 +100,10 @@
type: "compile",
});
}}
>Compile
>{$_("gameControls_button_compile")}
<!-- NOTE - this is a bug in flowbite-svelte, it's not replacing the default class but just appending -->
<Helper helperClass="!text-neutral-400 !text-xs"
>Rebuild the game. (ie. after modifying OpenGOAL source code)
>{$_("gameControls_button_compile_helpText")}
</Helper></DropdownItem
>
</Dropdown>
@ -116,12 +117,12 @@
<DropdownItem
on:click={async () => {
await openDir(settingsDir);
}}>Open&nbsp;Settings&nbsp;Folder</DropdownItem
}}>{$_("gameControls_button_openSettingsFolder")}</DropdownItem
>
<DropdownItem
on:click={async () => {
await openDir(savesDir);
}}>Open&nbsp;Saves&nbsp;Folder</DropdownItem
}}>{$_("gameControls_button_openSavesFolder")}</DropdownItem
>
<DropdownDivider />
<!-- TODO - verify installation -->
@ -129,14 +130,14 @@
<DropdownItem
on:click={async () => {
await resetGameSettings(getInternalName(activeGame));
}}>Reset Settings</DropdownItem
}}>{$_("gameControls_button_resetSettings")}</DropdownItem
>
<DropdownItem
on:click={async () => {
// Get confirmation
// TODO - probably move these confirms into the actual launcher itself
const confirmed = await confirm(
"Are you sure you want to uninstall?",
$_("gameControls_button_uninstall_confirmation"),
{ title: "OpenGOAL Launcher", type: "warning" }
);
if (confirmed) {
@ -144,8 +145,9 @@
dispatch("change");
}
}}
>Uninstall<Helper helperClass="!text-neutral-400 !text-xs"
>This will not delete any saves or settings</Helper
>{$_("gameControls_button_uninstall")}<Helper
helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_uninstall_helpText")}</Helper
></DropdownItem
>
</Dropdown>

View file

@ -1,15 +1,18 @@
<script lang="ts">
import { _ } from "svelte-i18n";
</script>
<div class="flex flex-col h-full justify-center items-center p-5 text-center">
<h1 class="text-2xl font-black mb-5 text-outline">
Jak 2 is Currently in Development
{$_("temp_jak2_indev_header")}
</h1>
<p class="mb-10">
In the meantime, check out our latest progress reports showcasing what we've
accomplished so far
{$_("temp_jak2_indev_subheader")}
</p>
<a
class="font-bold text-orange-400 hover:text-orange-600"
href="https://opengoal.dev/blog/tags/progress-report"
target="_blank"
rel="noreferrer">Progress Reports</a
rel="noreferrer">{$_("temp_jak2_indev_progressReports")}</a
>
</div>

View file

@ -1,16 +1,18 @@
<script>
import { Button } from "flowbite-svelte";
import { _ } from "svelte-i18n";
</script>
<div class="flex flex-col h-full justify-center items-center p-5 text-center">
<h1 class="text-2xl font-black mb-5 text-outline">
No Tooling Version Configured!
{$_("gameControls_noToolingSet_header")}
</h1>
<p class="mb-10">
Head over to the following settings page to download the latest release
{$_("gameControls_noToolingSet_subheader")}
</p>
<Button
btnClass="border-solid border-2 border-slate-500 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
href="/settings/versions">Set Version</Button
href="/settings/versions"
>{$_("gameControls_noToolingSet_button_setVersion")}</Button
>
</div>

View file

@ -14,6 +14,7 @@
} from "$lib/rpc/binaries";
import { finalizeInstallation } from "$lib/rpc/config";
import { generateSupportPackage } from "$lib/rpc/support";
import { _ } from "svelte-i18n";
export let activeGame: SupportedGame;
export let jobType: Job;
@ -32,11 +33,11 @@
progressTracker.init([
{
status: "queued",
label: "Decompile",
label: $_("setup_decompile"),
},
{
status: "queued",
label: "Done",
label: $_("setup_done"),
},
]);
progressTracker.start();
@ -54,11 +55,11 @@
progressTracker.init([
{
status: "queued",
label: "Compile",
label: $_("setup_compile"),
},
{
status: "queued",
label: "Done",
label: $_("setup_done"),
},
]);
progressTracker.start();
@ -76,19 +77,19 @@
progressTracker.init([
{
status: "queued",
label: "Copy Files",
label: $_("setup_copyFiles"),
},
{
status: "queued",
label: "Decompile",
label: $_("setup_decompile"),
},
{
status: "queued",
label: "Compile",
label: $_("setup_compile"),
},
{
status: "queued",
label: "Done",
label: $_("setup_done"),
},
]);
progressTracker.start();
@ -137,7 +138,8 @@
<div class="flex flex-row gap-2">
<Button
btnClass="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => dispatchCompleteJob()}>Continue</Button
on:click={async () => dispatchCompleteJob()}
>{$_("setup_button_continue")}</Button
>
</div>
</div>
@ -146,13 +148,13 @@
<div class="flex flex-row gap-2">
<Alert color="red" class="dark:bg-slate-900 flex-grow" accent={true}>
<span class="font-medium text-red-500"
>Installation has failed!
>{$_("setup_installationFailed")}
</span><span class="text-white"> {installationError}</span>
</Alert>
<Button
btnClass="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => await generateSupportPackage()}
>Get Support Package</Button
>{$_("setup_button_getSupportPackage")}</Button
>
</div>
</div>

View file

@ -1,10 +1,6 @@
<script lang="ts">
import Progress from "./Progress.svelte";
import {
getGameTitle,
getInternalName,
type SupportedGame,
} from "$lib/constants";
import { getInternalName, type SupportedGame } from "$lib/constants";
import LogViewer from "./LogViewer.svelte";
import Requirements from "./Requirements.svelte";
import { createEventDispatcher, onMount } from "svelte";
@ -25,6 +21,7 @@
import { progressTracker } from "$lib/stores/ProgressStore";
import { generateSupportPackage } from "$lib/rpc/support";
import { isOpenGLVersionSupported } from "$lib/sidecars/glewinfo";
import { _ } from "svelte-i18n";
export let activeGame: SupportedGame;
@ -61,19 +58,19 @@
progressTracker.init([
{
status: "queued",
label: "Extract and Verify",
label: $_("setup_extractAndVerify"),
},
{
status: "queued",
label: "Decompile",
label: $_("setup_decompile"),
},
{
status: "queued",
label: "Compile",
label: $_("setup_compile"),
},
{
status: "queued",
label: "Done",
label: $_("setup_done"),
},
]);
// TODO - make this cleaner
@ -130,7 +127,8 @@
<div class="flex flex-row gap-2">
<Button
btnClass="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => await dispatchSetupEvent()}>Continue</Button
on:click={async () => await dispatchSetupEvent()}
>{$_("setup_button_continue")}</Button
>
</div>
</div>
@ -139,13 +137,13 @@
<div class="flex flex-row gap-2">
<Alert color="red" class="dark:bg-slate-900 flex-grow" accent={true}>
<span class="font-medium text-red-500"
>Installation has failed!
>{$_("setup_installationFailed")}
</span><span class="text-white"> {installationError}</span>
</Alert>
<Button
btnClass="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => await generateSupportPackage()}
>Get Support Package</Button
>{$_("setup_button_getSupportPackage")}</Button
>
</div>
</div>
@ -155,12 +153,13 @@
<h1
class="tracking-tighter text-2xl font-bold pb-3 text-orange-500 text-outline"
>
{getGameTitle(activeGame)}
{$_(`gameName_${getInternalName(activeGame)}`)}
</h1>
<div class="flex flex-row gap-2">
<Button
btnClass="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => await install(false)}>Install via ISO</Button
on:click={async () => await install(false)}
>{$_("setup_button_installViaISO")}</Button
>
<!-- TODO - disabled for now, needs fixes in the extractor -->
<!-- <Button

View file

@ -3,6 +3,7 @@
import { VersionStore } from "$lib/stores/VersionStore";
import { Button, Card } from "flowbite-svelte";
import { createEventDispatcher } from "svelte";
import { _ } from "svelte-i18n";
export let activeGame: SupportedGame;
@ -17,25 +18,36 @@
<h5
class="mb-2 text-xl font-bold text-gray-900 dark:text-white text-center"
>
Version Mismatch Detected!
{$_("gameUpdate_versionMismatch_title")}
</h5>
<p class="text-base text-gray-500 dark:text-gray-400 mb-1">
The game is already installed with...
{$_("gameUpdate_versionMismatch_currentlyInstalled")}...
</p>
<ul class="list-disc list-inside mb-2">
<li>Version: <strong>{installedVersion}</strong></li>
<li>Type: <strong>{installedVersionFolder}</strong></li>
<li>
{$_("gameUpdate_versionMismatch_version")}:
<strong>{installedVersion}</strong>
</li>
<li>
{$_("gameUpdate_versionMismatch_type")}:
<strong>{installedVersionFolder}</strong>
</li>
</ul>
<p class="text-base text-gray-500 dark:text-gray-400 mb-1">
...but you currently have selected
...{$_("gameUpdate_versionMismatch_currentlySelected")}
</p>
<ul class="list-disc list-inside mb-5">
<li>Version: <strong>{$VersionStore.activeVersionName}</strong></li>
<li>Type: <strong>{$VersionStore.activeVersionType}</strong></li>
<li>
{$_("gameUpdate_versionMismatch_version")}:
<strong>{$VersionStore.activeVersionName}</strong>
</li>
<li>
{$_("gameUpdate_versionMismatch_type")}:
<strong>{$VersionStore.activeVersionType}</strong>
</li>
</ul>
<p class="mb-3">
You can either update the game to this new version (no save data will be
lost) or you can rollback your active version to match
{$_("gameUpdate_versionMismatch_nextSteps")}
</p>
<div
class="justify-center items-center space-y-4 sm:flex sm:space-y-0 sm:space-x-4"
@ -46,11 +58,12 @@
dispatch("job", {
type: "updateGame",
});
}}>Update Game</Button
}}>{$_("gameUpdate_versionMismatch_button_updateGame")}</Button
>
<Button
btnClass="border-solid border-2 border-slate-500 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
href="/settings/versions">Change Version</Button
href="/settings/versions"
>{$_("gameUpdate_versionMismatch_button_changeVersion")}</Button
>
</div>
</Card>

View file

@ -4,6 +4,7 @@
import { Accordion, AccordionItem } from "flowbite-svelte";
import { ansiSpan } from "ansi-to-span";
import escapeHtml from "escape-html";
import { _ } from "svelte-i18n";
function convertLogColors(text) {
return ansiSpan(escapeHtml(text)).replaceAll("\n", "<br/>");
@ -14,14 +15,14 @@
<AccordionItem class="bg-slate-900 rounded p-[1rem]">
<span slot="header" class="text-sm font-semibold text-white flex gap-2">
<Icon icon="mdi:file-document-outline" width={18} />
<span>Logs</span>
<span>{$_("setup_logs_header")}</span>
</span>
<div
slot="default"
class="bg-slate-900 px-4 max-h-60 overflow-y-scroll scrollbar"
>
<p class="py-4 text-clip overflow-hidden font-mono log-output">
...Last 250 Lines:
...{$_("setup_logs_truncation")}:
<br />
{@html convertLogColors($progressTracker.logs)}
</p>

View file

@ -4,6 +4,7 @@
type ProgressStatus,
} from "$lib/stores/ProgressStore";
import Icon from "@iconify/svelte";
$: progress = $progressTracker;
const iconContainerStyle =

View file

@ -2,6 +2,7 @@
import { onMount } from "svelte";
import { Alert } from "flowbite-svelte";
import { isAVXRequirementMet, isOpenGLRequirementMet } from "$lib/rpc/config";
import { _ } from "svelte-i18n";
let isAVXMet = false;
let isOpenGLMet = false;
@ -23,8 +24,7 @@
class="flex flex-col h-full justify-center items-center p-5 text-center gap-3"
>
<h1 class="text-xl font-black mb-5 text-outline">
Unfortunately, your system does not meet all the minimum requirements or we
were unable to check them
{$_("requirements_notMet_header")}
</h1>
<Alert
class="w-full text-start"
@ -33,21 +33,21 @@
color={alertColor(isAVXMet)}
>
{#if isAVXMet}
<span class="font-bold">Your CPU supports AVX</span>
<span class="font-bold">{$_("requirements_cpu_supportsAVX")}</span>
{:else if isAVXMet === undefined}
<span class="font-bold">Unable to verify if your CPU supports AVX</span>
<span class="font-bold">{$_("requirements_cpu_unableToCheckAVX")}</span>
{:else}
<span class="font-bold">Your CPU does not support AVX</span>
<span class="font-bold">{$_("requirements_cpu_doesNotSupportAVX")}</span>
<ul class="font-medium list-disc list-inside">
<li>This cannot be fixed without upgrading to a newer CPU</li>
<li>AVX support has been fairly standard since 2011</li>
<li>{$_("requirements_cpu_avxExplanation_1")}</li>
<li>{$_("requirements_cpu_avxExplanation_2")}</li>
<li>
<a
class="font-bold text-blue-500"
target="_blank"
rel="noreferrer"
href="https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#CPUs_with_AVX"
>Click here for more information</a
>{$_("requirements_cpu_avxExplanation_3")}</a
>
</li>
</ul>
@ -60,26 +60,29 @@
color={alertColor(isOpenGLMet)}
>
{#if isOpenGLMet}
<span class="font-bold">Your GPU supports OpenGL 4.3</span>
<span class="font-bold">{$_("requirements_gpu_supportsOpenGL")}</span>
{:else if isOpenGLMet === undefined}
<span class="font-bold"
>Unable to verify if your GPU supports OpenGL 4.3</span
<span class="font-bold">{$_("requirements_gpu_unableToCheckOpenGL")}</span
>
{:else}
<span class="font-bold">Your GPU does not support OpenGL 4.3</span>
<span class="font-bold"
>{$_("requirements_gpu_doesNotSupportOpenGL")}</span
>
<ul class="font-medium list-disc list-inside">
<li>
Lookup your GPU <a
{$_("requirements_gpu_avxExplanation_1_preLink")}
<a
class="font-bold text-blue-500"
target="_blank"
rel="noreferrer"
href="https://www.techpowerup.com/gpu-specs/">here</a
> to see if it should be supported
href="https://www.techpowerup.com/gpu-specs/"
>{$_("requirements_gpu_avxExplanation_1_link")}</a
>
{$_("requirements_gpu_avxExplanation_1_postLink")}
</li>
<li>You can attempt to upgrade your GPU drivers</li>
<li>{$_("requirements_gpu_avxExplanation_2")}</li>
<li>
Otherwise, you will need to upgrade your GPU, most GPUs since 2012
support it
{$_("requirements_gpu_avxExplanation_3")}
</li>
</ul>
{/if}

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { appWindow } from "@tauri-apps/api/window";
import logo from "$assets/images/icon.png";
import logo from "$assets/images/icon.webp";
import { onMount } from "svelte";
import { getVersion } from "@tauri-apps/api/app";
import { Link } from "svelte-navigator";
@ -16,6 +16,7 @@
import { getLatestOfficialRelease } from "$lib/utils/github";
import { VersionStore } from "$lib/stores/VersionStore";
import { exceptionLog } from "$lib/rpc/logging";
import { _ } from "svelte-i18n";
let launcherVerison = null;
@ -94,7 +95,7 @@
</script>
<header
class="flex flex-row basis-1/10 bg-[#101010] pl-2 pr-4 pt-1 pb-1 items-center"
class="flex flex-row basis-1/10 bg-[#101010] pl-2 pr-4 pt-1 pb-1 items-center z-10"
data-tauri-drag-region
>
<div class="flex flex-row items-center space-x-2 pointer-events-none">
@ -132,7 +133,9 @@
? 'pointer-events-auto'
: 'invisible pointer-events-none'}"
>
<Link class="font-mono" to="/update">> Update Available!</Link>
<Link class="font-mono" to="/update"
>>&nbsp;{$_("header_updateAvailable")}</Link
>
</p>
<p
class="font-mono text-sm hover:text-orange-300 {$UpdateStore
@ -140,7 +143,8 @@
? 'pointer-events-auto'
: 'invisible pointer-events-none'}"
>
<Link class="font-mono " to="/settings/versions">> Update Available!</Link
<Link class="font-mono " to="/settings/versions"
>>&nbsp;{$_("header_updateAvailable")}</Link
>
</p>
</div>

View file

@ -4,6 +4,8 @@
import Icon from "@iconify/svelte";
import { link, useLocation } from "svelte-navigator";
import { Tooltip } from "flowbite-svelte";
import { SupportedGame, getInternalName } from "$lib/constants";
import { _ } from "svelte-i18n";
const location = useLocation();
$: $location.pathname;
@ -50,7 +52,7 @@
>
<img src={logoJak1} alt="Jak - The Precursor Legacy" />
<Tooltip placement="right"
>Jak&nbsp;and&nbsp;Daxter:&nbsp;The&nbsp;Precursor&nbsp;Legacy</Tooltip
>{$_(`gameName_${getInternalName(SupportedGame.Jak1)}`)}</Tooltip
>
</a>
</li>
@ -61,7 +63,9 @@
use:link
>
<img src={logoJak2} alt="Jak 2" />
<Tooltip placement="right" style="dark">Jak 2</Tooltip>
<Tooltip placement="right" style="dark"
>{$_(`gameName_${getInternalName(SupportedGame.Jak2)}`)}</Tooltip
>
</a>
</li>
<li class="fixed bottom-24 left-6">
@ -71,7 +75,9 @@
use:link
>
<Icon icon="material-symbols:settings" width={36} height={36} />
<Tooltip placement="right" style="dark">Settings</Tooltip>
<Tooltip placement="right" style="dark"
>{$_("sidebar_settings")}</Tooltip
>
</a>
</li>
@ -82,8 +88,7 @@
use:link
>
<Icon icon="material-symbols:contact-support" width={36} height={36} />
<Tooltip placement="right" style="dark">Support&nbsp;&&nbsp;FAQ</Tooltip
>
<Tooltip placement="right" style="dark">{$_("sidebar_help")}</Tooltip>
</a>
</li>
</ul>

View file

@ -16,7 +16,7 @@
* {
-webkit-user-drag: none;
font-family: "Inter", sans-serif;
font-family: "Twemoji Country Flags", "Inter", sans-serif;
}
body {

View file

@ -1,5 +1,3 @@
import { TranslatedStrings } from "./translations/translations";
export const enum SupportedGame {
Jak1 = "Jak 1",
Jak2 = "Jak 2",
@ -7,17 +5,6 @@ export const enum SupportedGame {
JakX = "Jak X",
}
// TODO - we should really just have `SupportedGame` be a class instead of an enum
// then these could just be methods
export function getGameTitle(game: SupportedGame) {
switch (game) {
case SupportedGame.Jak1:
return TranslatedStrings.jak1_gameName;
default:
return "";
}
}
export function fromRoute(gameName: string): SupportedGame {
switch (gameName) {
case "jak1":

40
src/lib/i18n/i18n.ts Normal file
View file

@ -0,0 +1,40 @@
import { addMessages, init, register } from "svelte-i18n";
interface Locale {
id: string;
flag: string;
localizedName: string;
}
export const AVAILABLE_LOCALES: Locale[] = [
{
id: "en-US",
flag: "🇺🇸",
localizedName: "English",
},
];
export async function initLocales(async: boolean) {
if (async) {
for (const locale of AVAILABLE_LOCALES) {
register(
locale.id,
() => import(`../../assets/translations/${locale.id}.json`)
);
}
} else {
for (const locale of AVAILABLE_LOCALES) {
addMessages(
locale.id,
await import(`../../assets/translations/${locale.id}.json`)
);
}
}
const initPromise = init({
fallbackLocale: "en-US",
initialLocale: "en-US",
});
if (!async) {
await initPromise;
}
}

View file

@ -2,6 +2,7 @@ import { toastStore } from "$lib/stores/ToastStore";
import { invoke } from "@tauri-apps/api/tauri";
import { errorLog, exceptionLog } from "./logging";
import type { VersionFolders } from "./versions";
import { locale } from "svelte-i18n";
export async function oldDataDirectoryExists(): Promise<boolean> {
try {
@ -147,3 +148,21 @@ export async function saveActiveVersionChange(
return false;
}
}
export async function getLocale(): Promise<String | null> {
try {
return await invoke("get_locale", {});
} catch (e) {
exceptionLog("Unable to get locale", e);
return "en-US";
}
}
export async function setLocale(locale_string: string): Promise<void> {
try {
await invoke("set_locale", { locale: locale_string });
locale.set(locale_string);
} catch (e) {
exceptionLog("Unable to set locale", e);
}
}

View file

@ -1,181 +0,0 @@
// Code Generated from - https://app.quicktype.io/
// Don't Hand Modify!
// To parse this data:
//
// import { Convert, TranslationSchema } from "./file";
//
// const translationSchema = Convert.toTranslationSchema(json);
//
// These functions will throw an error if the JSON doesn't
// match the expected interface, even if the JSON is valid.
export interface TranslationSchema {
jak1_gameName: string;
}
// Converts JSON strings to/from your types
// and asserts the results of JSON.parse at runtime
export class Convert {
public static toTranslationSchema(json: string): TranslationSchema {
return cast(JSON.parse(json), r("TranslationSchema"));
}
public static translationSchemaToJson(value: TranslationSchema): string {
return JSON.stringify(uncast(value, r("TranslationSchema")), null, 2);
}
}
function invalidValue(typ: any, val: any, key: any = ""): never {
if (key) {
throw Error(
`Invalid value for key "${key}". Expected type ${JSON.stringify(
typ
)} but got ${JSON.stringify(val)}`
);
}
throw Error(
`Invalid value ${JSON.stringify(val)} for type ${JSON.stringify(typ)}`
);
}
function jsonToJSProps(typ: any): any {
if (typ.jsonToJS === undefined) {
const map: any = {};
typ.props.forEach((p: any) => (map[p.json] = { key: p.js, typ: p.typ }));
typ.jsonToJS = map;
}
return typ.jsonToJS;
}
function jsToJSONProps(typ: any): any {
if (typ.jsToJSON === undefined) {
const map: any = {};
typ.props.forEach((p: any) => (map[p.js] = { key: p.json, typ: p.typ }));
typ.jsToJSON = map;
}
return typ.jsToJSON;
}
function transform(val: any, typ: any, getProps: any, key: any = ""): any {
function transformPrimitive(typ: string, val: any): any {
if (typeof typ === typeof val) return val;
return invalidValue(typ, val, key);
}
function transformUnion(typs: any[], val: any): any {
// val must validate against one typ in typs
const l = typs.length;
for (let i = 0; i < l; i++) {
const typ = typs[i];
try {
return transform(val, typ, getProps);
} catch (_) {}
}
return invalidValue(typs, val);
}
function transformEnum(cases: string[], val: any): any {
if (cases.indexOf(val) !== -1) return val;
return invalidValue(cases, val);
}
function transformArray(typ: any, val: any): any {
// val must be an array with no invalid elements
if (!Array.isArray(val)) return invalidValue("array", val);
return val.map((el) => transform(el, typ, getProps));
}
function transformDate(val: any): any {
if (val === null) {
return null;
}
const d = new Date(val);
if (isNaN(d.valueOf())) {
return invalidValue("Date", val);
}
return d;
}
function transformObject(
props: { [k: string]: any },
additional: any,
val: any
): any {
if (val === null || typeof val !== "object" || Array.isArray(val)) {
return invalidValue("object", val);
}
const result: any = {};
Object.getOwnPropertyNames(props).forEach((key) => {
const prop = props[key];
const v = Object.prototype.hasOwnProperty.call(val, key)
? val[key]
: undefined;
result[prop.key] = transform(v, prop.typ, getProps, prop.key);
});
Object.getOwnPropertyNames(val).forEach((key) => {
if (!Object.prototype.hasOwnProperty.call(props, key)) {
result[key] = transform(val[key], additional, getProps, key);
}
});
return result;
}
if (typ === "any") return val;
if (typ === null) {
if (val === null) return val;
return invalidValue(typ, val);
}
if (typ === false) return invalidValue(typ, val);
while (typeof typ === "object" && typ.ref !== undefined) {
typ = typeMap[typ.ref];
}
if (Array.isArray(typ)) return transformEnum(typ, val);
if (typeof typ === "object") {
return typ.hasOwnProperty("unionMembers")
? transformUnion(typ.unionMembers, val)
: typ.hasOwnProperty("arrayItems")
? transformArray(typ.arrayItems, val)
: typ.hasOwnProperty("props")
? transformObject(getProps(typ), typ.additional, val)
: invalidValue(typ, val);
}
// Numbers can be parsed by Date but shouldn't be.
if (typ === Date && typeof val !== "number") return transformDate(val);
return transformPrimitive(typ, val);
}
function cast<T>(val: any, typ: any): T {
return transform(val, typ, jsonToJSProps);
}
function uncast<T>(val: T, typ: any): any {
return transform(val, typ, jsToJSONProps);
}
function a(typ: any) {
return { arrayItems: typ };
}
function u(...typs: any[]) {
return { unionMembers: typs };
}
function o(props: any[], additional: any) {
return { props, additional };
}
function m(additional: any) {
return { props: [], additional };
}
function r(name: string) {
return { ref: name };
}
const typeMap: any = {
TranslationSchema: o(
[{ json: "jak1_gameName", js: "jak1_gameName", typ: "" }],
false
),
};

View file

@ -1,17 +0,0 @@
import { Convert } from "./translation_schema";
import type { TranslationSchema } from "./translation_schema";
import english from "$assets/translations/english.json";
let supportedTranslations = ["english"];
export let TranslatedStrings: TranslationSchema;
export function loadTranslations(language: string) {
if (!supportedTranslations.includes(language)) {
}
// TODO - would prefer to import this by a raw path but have to import
// for vite reasons -- maybe there is a different way to ensure they are bundled?
if (language === "english") {
TranslatedStrings = Convert.toTranslationSchema(JSON.stringify(english));
}
}

View file

@ -1,8 +1,12 @@
import { initLocales } from "$lib/i18n/i18n";
import "./app.postcss";
import { loadTranslations } from "$lib/translations/translations";
import App from "./App.svelte";
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
loadTranslations("english");
polyfillCountryFlagEmojis();
// Register Translations
initLocales(true);
const app = new App({
target: document.getElementById("app"),

View file

@ -5,6 +5,7 @@
import { openDir } from "$lib/rpc/window";
import { onMount } from "svelte";
import { appConfigDir } from "@tauri-apps/api/path";
import { _ } from "svelte-i18n";
let appDir = undefined;
let downloadingPackage = false;
@ -15,10 +16,9 @@
</script>
<div class="flex flex-col h-full bg-slate-900 p-4 gap-3">
<h1 class="font-semibold text-xl text-orange-500">Support & FAQ</h1>
<h1 class="font-semibold text-xl text-orange-500">{$_("help_header")}</h1>
<p class="text-sm">
If you are reporting an issue or asking for help, download the following
support package and attach it in your Discord thread or GitHub issue.
{$_("help_header")}
</p>
<div class="flex flex-row mt-1 gap-2">
<Button
@ -32,25 +32,22 @@
{#if downloadingPackage}
<Spinner class="text-sm mb-0.5 mr-1" size="4" color="white" />
{/if}
Download Support Package</Button
{$_("help_button_downloadPackage")}</Button
>
{#if appDir !== undefined}
<Button
btnClass="flex items-center border-solid rounded bg-white hover:bg-orange-400 text-sm text-slate-900 font-semibold px-4 py-2"
on:click={() => {
openDir(appDir);
}}>Open Log Folder</Button
}}>{$_("help_button_openLogFolder")}</Button
>
{/if}
</div>
<p class="mt-3 text-sm">
You can either ask a question on our Discord, or create a GitHub issue with
as much detail as possible.
{$_("help_description_createAnIssue")}
</p>
<p class="text-sm">
In either location, please do a quick search to see if the question has
already been answered before
{$_("help_description_duplicateReminder")}
</p>
<div class="flex flex-row gap-2">
<Button
@ -69,16 +66,18 @@
href="https://github.com/open-goal/launcher/issues/new/choose"
target="_blank"
rel="noreferrer noopener"
><Icon class="inline-block" icon="mdi:github" width={20} />&nbsp;Report
Launcher Issue</Button
><Icon class="inline-block" icon="mdi:github" width={20} />&nbsp;{$_(
"help_button_reportLauncherIssue"
)}</Button
>
<Button
btnClass="flex items-center border-solid rounded bg-white hover:bg-orange-400 text-sm text-slate-900 font-semibold px-4 py-2"
href="https://github.com/open-goal/jak-project/issues/new/choose"
target="_blank"
rel="noreferrer noopener"
><Icon class="inline-block" icon="mdi:github" width={20} />&nbsp;Report
Game Issue</Button
><Icon class="inline-block" icon="mdi:github" width={20} />&nbsp;{$_(
"help_button_reportGameIssue"
)}</Button
>
</div>
<div class="flex mt-auto justify-end">

View file

@ -4,6 +4,7 @@
import Folders from "./settings/Folders.svelte";
import Versions from "./settings/Versions.svelte";
import General from "./settings/General.svelte";
import { _ } from "svelte-i18n";
const params = useParams();
$: activeTab = $params["tab"];
@ -24,7 +25,7 @@
>
<TabItem
open={!activeTab || activeTab === "general"}
title="General"
title={$_("settings_tabs_general")}
activeClasses={tabItemActiveClasses}
inactiveClasses={tabItemInactiveClasses}
>
@ -32,7 +33,7 @@
</TabItem>
<TabItem
open={!activeTab || activeTab === "folders"}
title="Folders"
title={$_("settings_tabs_folders")}
activeClasses={tabItemActiveClasses}
inactiveClasses={tabItemInactiveClasses}
>
@ -40,7 +41,7 @@
</TabItem>
<TabItem
open={activeTab === "versions"}
title="Version Management"
title={$_("settings_tabs_versions")}
activeClasses={tabItemActiveClasses}
inactiveClasses={tabItemInactiveClasses}
>

View file

@ -14,6 +14,7 @@
} from "flowbite-svelte";
import { UpdateStore } from "$lib/stores/AppStore";
import Icon from "@iconify/svelte";
import { _ } from "svelte-i18n";
$: launcherUpdateInfo = $UpdateStore?.launcher;
@ -34,14 +35,15 @@
<div class="flex flex-col h-full bg-slate-900 p-4 gap-3 overflow-y-auto pb-20">
{#if $UpdateStore.launcher.updateAvailable}
<h1 class="font-semibold text-xl text-orange-500">
Launcher Update Available
{$_("settings_tabs_general")}
</h1>
<p>
Version: <strong>{launcherUpdateInfo.versionNumber}</strong>
{$_("update_versionLabel")}:&nbsp;<strong
>{launcherUpdateInfo.versionNumber}</strong
>
</p>
<p class="text-sm">
View the changes below and click the button to update to the latest
version. The launcher will restart when finished.
{$_("update_description")}
</p>
<div class="flex flex-row mt-1 gap-3">
<Button
@ -52,13 +54,16 @@
{#if updating}
<Spinner class="mr-3" size="4" color="white" />
{/if}
Update Launcher
{$_("update_button_doUpdate")}
</Button>
<Button
btnClass="flex-shrink border-solid rounded bg-white hover:bg-orange-400 text-sm text-slate-900 font-semibold px-5 py-2"
on:click={() => (showChanges = !showChanges)}>View Changelog</Button
on:click={() => (showChanges = !showChanges)}
>{$_("update_button_viewChangelog")}g</Button
>
<Toggle checked={showDependencyChanges}
>{$_("update_button_hideDependencyChanges")}</Toggle
>
<Toggle checked={showDependencyChanges}>Dependency Changes</Toggle>
</div>
{#if showChanges}
<Table hoverable={true}>
@ -66,9 +71,15 @@
class="p-2 font-semibold text-right text-gray-900 bg-white dark:text-white dark:bg-gray-800"
/>
<TableHead>
<TableHeadCell>Contributor</TableHeadCell>
<TableHeadCell>Description</TableHeadCell>
<TableHeadCell>Pull Request</TableHeadCell>
<TableHeadCell
>{$_("update_changelog_header_contributor")}</TableHeadCell
>
<TableHeadCell
>{$_("update_changelog_header_description")}</TableHeadCell
>
<TableHeadCell
>{$_("update_changelog_header_pullRequest")}</TableHeadCell
>
</TableHead>
<TableBody tableBodyClass="divide-y">
{#each launcherUpdateInfo.changeLog["changes"].filter((note) => {
@ -105,6 +116,8 @@
</Table>
{/if}
{:else}
<h1 class="font-semibold text-xl text-orange-500">You're Up to Date!</h1>
<h1 class="font-semibold text-xl text-orange-500">
{$_("update_alreadyUpToDate")}
</h1>
{/if}
</div>

View file

@ -7,6 +7,7 @@
import { folderPrompt } from "$lib/utils/file";
import { Label, Input } from "flowbite-svelte";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
let currentInstallationDirectory = "";
@ -17,12 +18,16 @@
<div class="flex flex-col gap-2 mt-2">
<div>
<Label for="default-input" class="block mb-2">Installation Directory</Label>
<Label for="default-input" class="block mb-2"
>{$_("settings_folders_installationDir")}</Label
>
<Input
id="default-input"
placeholder={currentInstallationDirectory}
on:click={async () => {
const newInstallDir = await folderPrompt("Pick an Installation Folder");
const newInstallDir = await folderPrompt(
$_("settings_folders_installationDir_prompt")
);
if (
newInstallDir !== undefined &&
newInstallDir !== currentInstallationDirectory

View file

@ -1,27 +1,66 @@
<script lang="ts">
import { AVAILABLE_LOCALES } from "$lib/i18n/i18n";
import {
getInstallationDirectory,
getLocale,
resetLauncherSettingsToDefaults,
setLocale,
} from "$lib/rpc/config";
import { getActiveVersion, getActiveVersionFolder } from "$lib/rpc/versions";
import { VersionStore } from "$lib/stores/VersionStore";
import { Button } from "flowbite-svelte";
import { Button, Helper, Label, Select } from "flowbite-svelte";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
let currentInstallationDirectory = "";
let currentLocale;
let availableLocales = [];
onMount(async () => {
currentInstallationDirectory = await getInstallationDirectory();
for (const locale of AVAILABLE_LOCALES) {
availableLocales = [
...availableLocales,
{
value: locale.id,
name: `${locale.flag} ${locale.localizedName}`,
},
];
}
currentLocale = await getLocale();
});
</script>
<div class="flex flex-col gap-2 mt-2">
<div class="flex flex-col gap-5 mt-2">
<div>
<Label
>{$_("settings_general_localeChange")}
<Select
class="mt-2"
items={availableLocales}
bind:value={currentLocale}
on:change={async (evt) => {
setLocale(evt.target.value);
}}
/>
</Label>
<Helper class="text-sm mt-2"
>{$_("settings_general_localeChange_helper_1")}
<a
class=" text-orange-400 hover:text-orange-600"
href="https://crowdin.com/project/opengoal-launcher"
target="_blank"
rel="noreferrer">{$_("settings_general_localeChange_helper_link")}</a
>
{$_("settings_general_localeChange_helper_2")}</Helper
>
</div>
<div>
<Button
btnClass="flex-shrink border-solid rounded bg-white hover:bg-orange-400 text-sm text-slate-900 font-semibold px-5 py-2"
on:click={async () => {
const confirmed = await confirm(
"Are you sure you want to reset your launcher settings? This will reset your installation directory and any other settings you have changed. This will not affect your game files."
$_("settings_general_button_resetSettings_confirmation")
);
if (confirmed) {
const result = resetLauncherSettingsToDefaults();
@ -31,7 +70,7 @@
$VersionStore.activeVersionName = await getActiveVersion();
}
}
}}>Reset Launcher Settings</Button
}}>{$_("settings_general_button_resetSettings")}</Button
>
</div>
</div>

View file

@ -3,10 +3,11 @@
import OfficialVersions from "./versions/OfficialVersions.svelte";
import UnofficialVersions from "./versions/UnofficialVersions.svelte";
import DevelVersions from "./versions/DevelVersions.svelte";
import { _ } from "svelte-i18n";
</script>
<div>
<p class="text-sm mt-2 mb-2">Configure your active tooling version</p>
<p class="text-sm mt-2 mb-2">{$_("settings_versions_header")}</p>
<Tabs style="pill" contentClass="p-4 rounded-lg mt-0 pb-20 overflow-y-auto">
<OfficialVersions />
<UnofficialVersions />

View file

@ -11,6 +11,7 @@
import type { ReleaseInfo } from "$lib/utils/github";
import { VersionStore } from "$lib/stores/VersionStore";
import { saveActiveVersionChange } from "$lib/rpc/config";
import { _ } from "svelte-i18n";
let versionsLoaded = false;
@ -80,8 +81,8 @@
<VersionList
initiallyOpen={false}
name="Development"
description="This list serves as a convenient area to stage, manage and test new releases (either official or unofficial) This list will always require manual management via it's respective folder"
name={$_("settings_versions_devel_tabName")}
description={$_("settings_versions_devel_description")}
releaseList={releases}
loaded={versionsLoaded}
releaseType="devel"

View file

@ -13,6 +13,7 @@
import { VersionStore } from "$lib/stores/VersionStore";
import { UpdateStore } from "$lib/stores/AppStore";
import { saveActiveVersionChange } from "$lib/rpc/config";
import { _ } from "svelte-i18n";
let versionsLoaded = false;
let releases: ReleaseInfo[] = [];
@ -159,8 +160,8 @@
<VersionList
initiallyOpen={true}
name="Official"
description="Official versions are from the `jak-project` GitHub repository"
name={$_("settings_versions_official_tabName")}
description={$_("settings_versions_official_description")}
releaseList={releases}
loaded={versionsLoaded}
releaseType="official"

View file

@ -11,6 +11,7 @@
import type { ReleaseInfo } from "$lib/utils/github";
import { VersionStore } from "$lib/stores/VersionStore";
import { saveActiveVersionChange } from "$lib/rpc/config";
import { _ } from "svelte-i18n";
let versionsLoaded = false;
@ -83,9 +84,8 @@
<VersionList
initiallyOpen={false}
name="Unofficial"
description="Unofficial versions are typically modified `jak-project` releases to enable changes or new content. These are not supported by the OpenGOAL team and will have to be
manually added to the folder at this time"
name={$_("settings_versions_unofficial_tabName")}
description={$_("settings_versions_unofficial_description")}
releaseList={releases}
loaded={versionsLoaded}
releaseType="unofficial"

View file

@ -19,6 +19,7 @@
TableHeadCell,
} from "flowbite-svelte";
import { createEventDispatcher } from "svelte";
import { _ } from "svelte-i18n";
export let name: string;
export let description: string;
@ -71,7 +72,7 @@
icon="material-symbols:save"
width="20"
height="20"
alt="save version change"
alt={$_("settings_versions_icon_save_altText")}
/>
</Button>
{/if}
@ -83,7 +84,7 @@
icon="material-symbols:refresh"
width="20"
height="20"
alt="refresh version list"
alt={$_("settings_versions_icon_refresh_altText")}
/>
</Button>
<Button
@ -94,7 +95,7 @@
icon="material-symbols:folder-open-rounded"
width="20"
height="20"
alt="open version folder"
alt={$_("settings_versions_icon_openFolder_altText")}
/>
</Button>
</div>
@ -107,9 +108,15 @@
<TableHeadCell>
<span class="sr-only">Controls</span>
</TableHeadCell>
<TableHeadCell>Version</TableHeadCell>
<TableHeadCell>Date</TableHeadCell>
<TableHeadCell>Changes</TableHeadCell>
<TableHeadCell
>{$_("settings_versions_table_header_version")}</TableHeadCell
>
<TableHeadCell
>{$_("settings_versions_table_header_date")}</TableHeadCell
>
<TableHeadCell
>{$_("settings_versions_table_header_changes")}</TableHeadCell
>
</TableHead>
<TableBody tableBodyClass="divide-y">
{#each releaseList as release (release.version)}
@ -154,6 +161,7 @@
width="24"
height="24"
color="red"
alt={$_("settings_versions_icon_removeVersion_altText")}
/>
{:else if release.pendingAction}
<Spinner color="yellow" size={"6"} />
@ -163,6 +171,7 @@
color="#00d500"
width="24"
height="24"
alt={$_("settings_versions_icon_downloadVersion_altText")}
/>
{/if}
</Button>
@ -187,6 +196,7 @@
icon="mdi:github"
width="24"
height="24"
alt={$_("settings_versions_icon_githubRelease_altText")}
/></a
>
{/if}

View file

@ -6,63 +6,70 @@
import {
deleteOldDataDirectory,
getInstallationDirectory,
getLocale,
oldDataDirectoryExists,
setInstallationDirectory,
setLocale,
} from "$lib/rpc/config";
import { AVAILABLE_LOCALES } from "$lib/i18n/i18n";
import { _ } from "svelte-i18n";
let currentProgress = 10;
let currentStatusText = "Reading Settings";
let currentStatusText = $_("splash_step_readingSettings");
let selectLocale = false;
let installationDirSet = true;
let stepError = undefined;
let oldDataDirToClean = false;
// Events
onMount(async () => {
currentStatusText = "Checking Directories";
// First, see if the user has selected a locale
await checkLocale();
});
// TODO - cleanup this code and make it easier to add steps like with
// the game setup
async function checkLocale() {
const locale = await getLocale();
if (locale === null) {
// Prompt the user to select a locale
selectLocale = true;
} else {
await checkDirectories();
}
}
async function checkDirectories() {
currentStatusText = $_("splash_step_checkingDirectories");
currentProgress = 15;
// Check to see if the install dir has been setup or not
const install_dir = await getInstallationDirectory();
if (install_dir === null) {
// Check to see if they have the old data directory, ask them if they'd like us to
// remove it
currentProgress = 25;
const hasOldDataDir = await oldDataDirectoryExists();
if (hasOldDataDir) {
oldDataDirToClean = true;
}
// If not -- let's ask the user to set one up
installationDirSet = false;
} else {
currentProgress = 25;
finishSplash();
}
});
async function selectInstallationFolder() {
// If not -- let's ask the user to set one up
// This is part of what allows for the user to install the games and such wherever they want
currentStatusText = "Pick an Installation Folder";
currentProgress = 25;
const newInstallDir = await folderPrompt("Pick an Installation Folder");
if (newInstallDir !== undefined) {
const result = await setInstallationDirectory(newInstallDir);
if (result !== null) {
stepError = result;
} else {
installationDirSet = true;
finishSplash();
}
}
}
async function finishSplash() {
currentProgress = 50;
currentStatusText = "Finishing Up";
currentStatusText = $_("splash_step_finishingUp");
await new Promise((res) => setTimeout(res, 1000));
currentProgress = 100;
await new Promise((res) => setTimeout(res, 500));
const errorClosing = await openMainWindow();
if (!errorClosing) {
currentStatusText = "Problem opening Launcher";
currentStatusText = $_("splash_step_errorOpening");
}
}
</script>
@ -72,8 +79,29 @@
<img src={logo} alt="" draggable="false" />
</div>
<div class="splash-contents no-pointer-events">
{#if oldDataDirToClean}
The old installation folder is no longer needed, delete it?
{#if selectLocale}
<span class="mb-1">{$_("splash_selectLocale")}</span>
<div class="splash-select">
<select
name="cars"
id="cars"
class="pointer-events emoji-font"
on:change={async (evt) => {
setLocale(evt.target.value);
selectLocale = false;
await checkDirectories();
}}
>
<option disabled selected value hidden />
{#each AVAILABLE_LOCALES as locale}
<option class="emoji-font" value={locale.id}
>{locale.flag}&nbsp;{locale.localizedName}</option
>
{/each}
</select>
</div>
{:else if oldDataDirToClean}
{$_("splash_deleteOldInstallDir")}
<br />
<span>
<button
@ -81,33 +109,53 @@
on:click={() => {
oldDataDirToClean = false;
deleteOldDataDirectory();
}}>Yes</button
}}>{$_("splash_button_deleteOldInstallDir_yes")}</button
>
<button
class="splash-button pointer-events"
on:click={() => {
oldDataDirToClean = false;
}}>No</button
}}>{$_("splash_button_deleteOldInstallDir_no")}</button
>
</span>
{:else if !installationDirSet}
{#if stepError !== undefined}
{stepError}
{:else}
No installation directory set!
{$_("splash_noInstallDirSet")}
{/if}
<br />
<button
class="splash-button pointer-events"
on:click={selectInstallationFolder}>Set Install Folder</button
on:click={async () => {
// This is part of what allows for the user to install the games and such wherever they want
currentStatusText = $_("splash_step_pickInstallFolder");
currentProgress = 25;
const newInstallDir = await folderPrompt(
$_("splash_button_setInstallFolder_prompt")
);
if (newInstallDir !== undefined) {
const result = await setInstallationDirectory(newInstallDir);
if (result !== null) {
stepError = result;
} else {
installationDirSet = true;
finishSplash();
}
}
}}>{$_("splash_button_setInstallFolder")}</button
>
{:else}
<div class="splash-status-text">{currentStatusText}</div>
{/if}
</div>
<div class="splash-bar">
<div class="splash-status-bar fg" style="width: {currentProgress}%" />
<div class="splash-status-bar bg" />
<div
data-tauri-drag-region
class="splash-status-bar fg"
style="width: {currentProgress}%"
/>
<div data-tauri-drag-region class="splash-status-bar bg" />
</div>
</div>
@ -133,7 +181,7 @@
height: 35%;
align-items: center;
justify-content: center;
font-family: "Roboto Mono", monospace;
font-family: "Twemoji Country Flags", "Roboto Mono", monospace;
font-size: 10pt;
text-align: center;
padding-left: 10px;
@ -218,4 +266,12 @@
.pointer-events {
pointer-events: auto;
}
.mb-1 {
margin-bottom: 1rem;
}
.emoji-font {
font-family: "Twemoji Country Flags", "Roboto Mono";
}
</style>

View file

@ -1,7 +1,12 @@
import { initLocales } from "$lib/i18n/i18n";
import App from "./Splash.svelte";
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
const app = new App({
target: document.getElementById("app"),
});
export default app;
// Register Translations
export default (async () => {
polyfillCountryFlagEmojis();
await initLocales(false);
return new App({
target: document.getElementById("app"),
});
})();

229
yarn.lock
View file

@ -137,6 +137,45 @@
resolved "https://registry.yarnpkg.com/@fiahfy/packbits/-/packbits-0.0.6.tgz#d93146683fa59f476c6ccb988eb1cf9d7c16e489"
integrity sha512-XuhF/edg+iIvXjkCWgfj6fWtRi/KrEPg2ILXj1l86EN4EssuOiPcLKgkMDr9cL8jTGtVd/MKUWW6Y0/ZVf1PGA==
"@formatjs/ecma402-abstract@1.11.4":
version "1.11.4"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz#b962dfc4ae84361f9f08fbce411b4e4340930eda"
integrity sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==
dependencies:
"@formatjs/intl-localematcher" "0.2.25"
tslib "^2.1.0"
"@formatjs/fast-memoize@1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz#e6f5aee2e4fd0ca5edba6eba7668e2d855e0fc21"
integrity sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==
dependencies:
tslib "^2.1.0"
"@formatjs/icu-messageformat-parser@2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz#a54293dd7f098d6a6f6a084ab08b6d54a3e8c12d"
integrity sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==
dependencies:
"@formatjs/ecma402-abstract" "1.11.4"
"@formatjs/icu-skeleton-parser" "1.3.6"
tslib "^2.1.0"
"@formatjs/icu-skeleton-parser@1.3.6":
version "1.3.6"
resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz#4ce8c0737d6f07b735288177049e97acbf2e8964"
integrity sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==
dependencies:
"@formatjs/ecma402-abstract" "1.11.4"
tslib "^2.1.0"
"@formatjs/intl-localematcher@0.2.25":
version "0.2.25"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz#60892fe1b271ec35ba07a2eb018a2dd7bca6ea3a"
integrity sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==
dependencies:
tslib "^2.1.0"
"@fullhuman/postcss-purgecss@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz#70db9a537c73750fbcf745b49573db758daba1d8"
@ -1255,6 +1294,17 @@ cli-boxes@^2.2.1:
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
cli-color@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.3.tgz#73769ba969080629670f3f2ef69a4bf4e7cc1879"
integrity sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==
dependencies:
d "^1.0.1"
es5-ext "^0.10.61"
es6-iterator "^2.0.3"
memoizee "^0.4.15"
timers-ext "^0.1.7"
cli-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
@ -1376,6 +1426,13 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
country-flag-emoji-polyfill@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/country-flag-emoji-polyfill/-/country-flag-emoji-polyfill-0.1.4.tgz#54b7ca61220c124b11d3091c46d16bd7f3ba0016"
integrity sha512-e20azlb9yHb3mpL3lAlhkidmJgB5TELpA8oe0DPlyIfnqXhAGBmgLDpC+mm+Envh57n1xPrOfkJtXq2CrpvoGQ==
dependencies:
is-emoji-supported "^0.0.5"
cross-env@7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
@ -1429,6 +1486,14 @@ currently-unhandled@^0.4.1:
dependencies:
array-find-index "^1.0.1"
d@1, d@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
dependencies:
es5-ext "^0.10.50"
type "^1.0.1"
debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -1523,7 +1588,7 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deepmerge@^4.3.1:
deepmerge@^4.2.2, deepmerge@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
@ -1666,16 +1731,52 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.62"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
dependencies:
es6-iterator "^2.0.3"
es6-symbol "^3.1.3"
next-tick "^1.1.0"
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
es6-iterator@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
dependencies:
d "1"
es5-ext "^0.10.35"
es6-symbol "^3.1.1"
es6-promise@^3.1.2:
version "3.3.1"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
dependencies:
d "^1.0.1"
ext "^1.1.2"
es6-weak-map@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53"
integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==
dependencies:
d "1"
es5-ext "^0.10.46"
es6-iterator "^2.0.3"
es6-symbol "^3.1.1"
esbuild@^0.17.5:
version "0.17.18"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.18.tgz#f4f8eb6d77384d68cd71c53eb6601c7efe05e746"
@ -1729,6 +1830,19 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
estree-walker@^2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
event-emitter@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
dependencies:
d "1"
es5-ext "~0.10.14"
exec-buffer@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/exec-buffer/-/exec-buffer-3.2.0.tgz#b1686dbd904c7cf982e652c1f5a79b1e5573082b"
@ -1813,6 +1927,13 @@ ext-name@^5.0.0:
ext-list "^2.0.0"
sort-keys-length "^1.0.0"
ext@^1.1.2:
version "1.7.0"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
dependencies:
type "^2.7.2"
external-editor@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
@ -2164,6 +2285,11 @@ globalthis@^1.0.1:
dependencies:
define-properties "^1.1.3"
globalyzer@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465"
integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==
globby@^12.0.0:
version "12.2.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22"
@ -2176,6 +2302,11 @@ globby@^12.0.0:
merge2 "^1.4.1"
slash "^4.0.0"
globrex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
got@12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4"
@ -2457,6 +2588,16 @@ inquirer@8.2.4:
through "^2.3.6"
wrap-ansi "^7.0.0"
intl-messageformat@^9.13.0:
version "9.13.0"
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-9.13.0.tgz#97360b73bd82212e4f6005c712a4a16053165468"
integrity sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==
dependencies:
"@formatjs/ecma402-abstract" "1.11.4"
"@formatjs/fast-memoize" "1.2.1"
"@formatjs/icu-messageformat-parser" "2.1.0"
tslib "^2.1.0"
into-stream@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6"
@ -2496,6 +2637,11 @@ is-core-module@^2.11.0:
dependencies:
has "^1.0.3"
is-emoji-supported@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/is-emoji-supported/-/is-emoji-supported-0.0.5.tgz#f22301b22c63d6322935e829f39dfa59d03a7fe2"
integrity sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@ -2586,6 +2732,11 @@ is-png@^2.0.0:
resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d"
integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==
is-promise@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
@ -2850,6 +3001,13 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
lru-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3"
integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==
dependencies:
es5-ext "~0.10.2"
magic-string@^0.27.0:
version "0.27.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
@ -2890,6 +3048,20 @@ matcher@^3.0.0:
dependencies:
escape-string-regexp "^4.0.0"
memoizee@^0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72"
integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==
dependencies:
d "^1.0.1"
es5-ext "^0.10.53"
es6-weak-map "^2.0.3"
event-emitter "^0.3.5"
is-promise "^2.2.2"
lru-queue "^0.1.0"
next-tick "^1.1.0"
timers-ext "^0.1.7"
meow@^3.3.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
@ -3007,6 +3179,11 @@ mkdirp@^0.5.1:
dependencies:
minimist "^1.2.6"
mri@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -3041,6 +3218,11 @@ napi-build-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
next-tick@1, next-tick@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@ -3853,6 +4035,13 @@ rxjs@^7.5.5:
dependencies:
tslib "^2.1.0"
sade@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
dependencies:
mri "^1.1.0"
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
@ -4230,6 +4419,18 @@ svelte-hmr@^0.15.1:
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.15.1.tgz#d11d878a0bbb12ec1cba030f580cd2049f4ec86b"
integrity sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==
svelte-i18n@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/svelte-i18n/-/svelte-i18n-3.6.0.tgz#0f345d066662dd8f46efefc0e867fb05b71c9dbd"
integrity sha512-qvvcMqHVCXJ5pHoQR5uGzWAW5vS3qB9mBq+W6veLZ6jkrzZGOziR+wyOUJsc59BupMh+Ae30qjOndFrRU6v5jA==
dependencies:
cli-color "^2.0.3"
deepmerge "^4.2.2"
estree-walker "^2"
intl-messageformat "^9.13.0"
sade "^1.8.1"
tiny-glob "^0.2.9"
svelte-navigator@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/svelte-navigator/-/svelte-navigator-3.2.2.tgz#4156c2388226e7c42b766ca4def63444ec58a8d2"
@ -4361,11 +4562,27 @@ timed-out@^4.0.0, timed-out@^4.0.1:
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==
timers-ext@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6"
integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==
dependencies:
es5-ext "~0.10.46"
next-tick "1"
timm@^1.6.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f"
integrity sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==
tiny-glob@^0.2.9:
version "0.2.9"
resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2"
integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==
dependencies:
globalyzer "0.1.0"
globrex "^0.1.2"
tinycolor2@^1.4.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
@ -4452,6 +4669,16 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
type@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
type@^2.7.2:
version "2.7.2"
resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
typedarray-to-buffer@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"