i18n/fonts: lazily download locale-specific fonts (#316)

This commit is contained in:
Tyler Wilding 2023-09-12 21:54:38 -06:00 committed by GitHub
parent bf4db0188e
commit 02f7b7b2d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 195 additions and 31 deletions

View file

@ -47,3 +47,6 @@ default = ["custom-protocol"]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = ["tauri/custom-protocol"]
[profile.release]
strip = true # Automatically strip symbols from the binary.

View file

@ -2,6 +2,7 @@ use serde::{Serialize, Serializer};
pub mod binaries;
pub mod config;
pub mod download;
pub mod features;
pub mod game;
pub mod logging;

View file

@ -0,0 +1,35 @@
use std::path::{Path, PathBuf};
use crate::util::{file::create_dir, network};
use super::CommandError;
#[tauri::command]
pub async fn download_file(url: String, destination: String) -> Result<(), CommandError> {
let download_path = PathBuf::from(&destination);
match download_path.parent() {
Some(parent) => {
if parent == Path::new("") {
return Err(CommandError::OSOperation(
"Unable to successfully download file".to_owned(),
));
} else {
create_dir(&parent.to_path_buf()).map_err(|_| {
CommandError::VersionManagement(format!(
"Unable to prepare destination folder '{}' for download",
parent.display()
))
})?;
}
}
None => {
return Err(CommandError::OSOperation(
"Unable to successfully download file".to_owned(),
));
}
}
network::download_file(&url, &download_path)
.await
.map_err(|_| CommandError::OSOperation("Unable to successfully download file".to_owned()))?;
Ok(())
}

View file

@ -158,6 +158,7 @@ fn main() {
commands::config::set_locale,
commands::config::get_enabled_texture_packs,
commands::config::cleanup_enabled_texture_packs,
commands::download::download_file,
commands::game::reset_game_settings,
commands::game::uninstall_game,
commands::game::get_furthest_game_milestone,

Binary file not shown.

Binary file not shown.

View file

@ -1,70 +1,60 @@
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Thin.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Thin.woff2");
font-weight: 100;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-ExtraLight.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-ExtraLight.woff2");
font-weight: 200;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Light.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Light.woff2");
font-weight: 300;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Regular.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Regular.woff2");
font-weight: 400;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Medium.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Medium.woff2");
font-weight: 500;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-SemiBold.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-SemiBold.woff2");
font-weight: 600;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Bold.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Bold.woff2");
font-weight: 700;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-ExtraBold.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-ExtraBold.woff2");
font-weight: 800;
}
@font-face {
font-family: "Noto Sans";
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Black.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSans-Black.woff2");
font-weight: 900;
}
@font-face {
font-family: "Noto Sans Mono";
src: url("/src/assets/fonts/Noto Sans/general/NotoSansMono-VariableFont.ttf");
}
@font-face {
font-family: "Noto Sans Arabic";
src: url("/src/assets/fonts/Noto Sans/NotoSansArabic-VariableFont.ttf");
}
@font-face {
font-family: "Noto Sans JP";
src: url("/src/assets/fonts/Noto Sans/NotoSansJP-VariableFont.ttf");
src: url("/src/assets/fonts/Noto Sans/general/NotoSansMono-VariableFont.woff2");
}
.font-mono {

View file

@ -1,10 +1,12 @@
import { addMessages, init, register } from "svelte-i18n";
interface Locale {
export interface Locale {
id: string;
flag: string;
localizedName: string;
fontFamily?: string;
fontFileName?: string;
fontDownloadUrl?: string;
}
// https://omniglot.com/language/names.htm
@ -19,6 +21,9 @@ export const AVAILABLE_LOCALES: Locale[] = [
flag: "🇸🇦",
localizedName: "العربية الفصحى",
fontFamily: "Noto Sans Arabic",
fontFileName: "NotoSansArabic-VariableFont.woff2",
fontDownloadUrl:
"https://github.com/open-goal/launcher-assets/releases/download/fonts%2Fv1.0.0/NotoSansArabic-VariableFont.woff2",
},
{
id: "ca-ES",
@ -90,11 +95,18 @@ export const AVAILABLE_LOCALES: Locale[] = [
flag: "🇯🇵",
localizedName: "日本語",
fontFamily: "Noto Sans JP",
fontFileName: "NotoSansJP-VariableFont.woff2",
fontDownloadUrl:
"https://github.com/open-goal/launcher-assets/releases/download/fonts%2Fv1.0.0/NotoSansJP-VariableFont.woff2",
},
{
id: "ko-KR",
flag: "🇰🇷",
localizedName: "한국어",
fontFamily: "Noto Sans KR",
fontFileName: "NotoSansKR-VariableFont_wght.woff2",
fontDownloadUrl:
"https://github.com/open-goal/launcher-assets/releases/download/fonts%2Fv1.0.0/NotoSansKR-VariableFont_wght.woff2",
},
{
id: "nl-NL",
@ -160,11 +172,19 @@ export const AVAILABLE_LOCALES: Locale[] = [
id: "zh-CN",
flag: "🇨🇳",
localizedName: "简体中文",
fontFamily: "Noto Sans SC",
fontFileName: "NotoSansSC-VariableFont_wght.woff2",
fontDownloadUrl:
"https://github.com/open-goal/launcher-assets/releases/download/fonts%2Fv1.0.0/NotoSansSC-VariableFont_wght.woff2",
},
{
id: "zh-TW",
flag: "🇹🇼",
localizedName: "繁體中文",
fontFamily: "Noto Sans TC",
fontFileName: "NotoSansTC-VariableFont_wght.woff2",
fontDownloadUrl:
"https://github.com/open-goal/launcher-assets/releases/download/fonts%2Fv1.0.0/NotoSansTC-VariableFont_wght.woff2",
},
];

View file

@ -3,7 +3,10 @@ import { locale as svelteLocale } from "svelte-i18n";
import { errorLog } from "./logging";
import { invoke_rpc } from "./rpc";
import type { VersionFolders } from "./versions";
import { AVAILABLE_LOCALES } from "$lib/i18n/i18n";
import { AVAILABLE_LOCALES, type Locale } from "$lib/i18n/i18n";
import { readBinaryFile, BaseDirectory, exists } from "@tauri-apps/api/fs";
import { appDataDir, join } from "@tauri-apps/api/path";
import { convertFileSrc } from "@tauri-apps/api/tauri";
export async function oldDataDirectoryExists(): Promise<boolean> {
return await invoke_rpc("has_old_data_directory", {}, () => false);
@ -106,23 +109,72 @@ export async function getLocale(): Promise<string | null> {
return await invoke_rpc("get_locale", {}, () => "en-US");
}
export async function localeSpecificFontAvailableForDownload(
localeId: string,
): Promise<Locale | undefined> {
let localeInfo = AVAILABLE_LOCALES.find((locale) => locale.id === localeId);
if (
localeInfo !== undefined &&
localeInfo.fontFileName !== undefined &&
localeInfo.fontDownloadUrl !== undefined
) {
const fontPath = await join(
await appDataDir(),
"fonts",
localeInfo.fontFileName,
);
const fontAlreadyDownloaded = await exists(fontPath);
if (fontAlreadyDownloaded) {
return undefined;
}
return localeInfo;
}
return undefined;
}
export async function setLocale(localeId: string): Promise<void> {
return await invoke_rpc(
"set_locale",
{ locale: localeId },
() => {},
null, // no toast
() => {
undefined, // no toast
async () => {
svelteLocale.set(localeId);
// Update CSS variable if needed
let localeInfo = AVAILABLE_LOCALES.find(
(locale) => locale.id === localeId,
);
if (localeInfo !== undefined && localeInfo.fontFamily !== undefined) {
document.documentElement.style.setProperty(
"--launcher-font-family",
localeInfo.fontFamily,
if (
localeInfo !== undefined &&
localeInfo.fontFamily !== undefined &&
localeInfo.fontFileName !== undefined
) {
// Dynamically get the font
const fontPath = await join(
await appDataDir(),
"fonts",
localeInfo.fontFileName,
);
const fontExists = await exists(fontPath);
if (fontExists) {
const assetUrl = convertFileSrc(fontPath);
var newFontStyle = document.createElement("style");
newFontStyle.appendChild(
document.createTextNode(
`@font-face {\nfont-family: "${localeInfo.fontFamily}";\nsrc: url('${assetUrl}');\n}\n`,
),
);
document.head.appendChild(newFontStyle);
document.documentElement.style.setProperty(
"--launcher-font-family",
localeInfo.fontFamily,
);
} else {
document.documentElement.style.setProperty(
"--launcher-font-family",
"Noto Sans",
);
}
} else {
document.documentElement.style.setProperty(
"--launcher-font-family",

13
src/lib/rpc/download.ts Normal file
View file

@ -0,0 +1,13 @@
import { invoke_rpc } from "./rpc";
export async function downloadFile(
url: String,
destination: String,
): Promise<void> {
await invoke_rpc(
"download_file",
{ url, destination },
() => {},
"Unable to download file",
);
}

View file

@ -1,24 +1,36 @@
<script lang="ts">
import { AVAILABLE_LOCALES } from "$lib/i18n/i18n";
import { AVAILABLE_LOCALES, type Locale } from "$lib/i18n/i18n";
import {
getBypassRequirements,
getInstallationDirectory,
getLocale,
localeSpecificFontAvailableForDownload,
resetLauncherSettingsToDefaults,
setBypassRequirements,
setLocale,
} from "$lib/rpc/config";
import { getActiveVersion, getActiveVersionFolder } from "$lib/rpc/versions";
import { VersionStore } from "$lib/stores/VersionStore";
import { Button, Helper, Label, Select, Toggle } from "flowbite-svelte";
import {
Button,
Helper,
Label,
Select,
Spinner,
Toggle,
} from "flowbite-svelte";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
import { confirm } from "@tauri-apps/api/dialog";
import { downloadFile } from "$lib/rpc/download";
import { appDataDir, join } from "@tauri-apps/api/path";
let currentInstallationDirectory = "";
let currentLocale;
let availableLocales = [];
let currentBypassRequirementsVal = false;
let localeFontForDownload: Locale | undefined = undefined;
let localeFontDownloading = false;
onMount(async () => {
currentInstallationDirectory = await getInstallationDirectory();
@ -33,6 +45,10 @@
}
currentLocale = await getLocale();
currentBypassRequirementsVal = await getBypassRequirements();
if (currentLocale !== null) {
localeFontForDownload =
await localeSpecificFontAvailableForDownload(currentLocale);
}
});
</script>
@ -45,7 +61,10 @@
items={availableLocales}
bind:value={currentLocale}
on:change={async (evt) => {
setLocale(evt.target.value);
await setLocale(evt.target.value);
localeFontForDownload = await localeSpecificFontAvailableForDownload(
evt.target.value,
);
}}
/>
</Label>
@ -59,6 +78,36 @@
>
{$_("settings_general_localeChange_helper_2")}</Helper
>
{#if localeFontForDownload !== undefined}
<Button
class="flex-shrink border-solid rounded bg-white hover:bg-orange-400 text-sm text-slate-900 font-semibold px-5 py-2 mt-2"
disabled={localeFontDownloading}
on:click={async () => {
if (
localeFontForDownload !== undefined &&
localeFontForDownload.fontDownloadUrl !== undefined &&
localeFontForDownload.fontFileName !== undefined
) {
localeFontDownloading = true;
const fontPath = await join(
await appDataDir(),
"fonts",
localeFontForDownload.fontFileName,
);
await downloadFile(localeFontForDownload.fontDownloadUrl, fontPath);
await setLocale(currentLocale);
localeFontForDownload =
await localeSpecificFontAvailableForDownload(currentLocale);
localeFontDownloading = false;
}
}}
>
{#if localeFontDownloading}
<Spinner class="mr-3" size="4" color="white" />
{/if}
Download Locale Specific Font
</Button>
{/if}
</div>
<div>
<Toggle