web: i18n system & navbar translations

dynamic page language and language dropdown!! finally!!
This commit is contained in:
wukko
2024-07-03 00:16:03 +06:00
parent d11874e57f
commit 9939f3b172
19 changed files with 229 additions and 37 deletions

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="viewport-fit=cover, width=device-width, height=device-height, initial-scale=1, maximum-scale=1" />

View File

@@ -0,0 +1,28 @@
<script lang="ts">
import settings, { updateSetting } from "$lib/settings";
import { t, locale, locales } from "$lib/i18n/translations";
import languages from "$i18n/languages.json";
$: currentSetting = $settings.appearance.language;
const updateLocale = (lang: string) => {
updateSetting({
appearance: {
language: lang as keyof typeof languages,
},
})
}
</script>
<select
id="setting-dropdown-appearance-language"
bind:value={$locale}
on:change={() => updateLocale($locale)}
>
{#each $locales as value}
<option value={value} selected={currentSetting === value}>
{$t(`languages.${value}`)}
</option>
{/each}
</select>

View File

@@ -1,4 +1,6 @@
<script lang="ts">
import { t } from "$lib/i18n/translations";
import CobaltLogo from "$components/sidebar/CobaltLogo.svelte";
import SidebarTab from "$components/sidebar/SidebarTab.svelte";
@@ -23,7 +25,7 @@
<svelte:window bind:innerWidth={screenWidth} />
<nav id="sidebar">
<nav id="sidebar" aria-label={$t("a11y.tabs.tabPanel")}>
<CobaltLogo />
<div id="sidebar-tabs">
<div id="sidebar-actions" class="sidebar-inner-container">

View File

@@ -1,33 +1,31 @@
<script lang="ts">
import { page } from "$app/stores";
import { t } from "$lib/i18n/translations";
export let tabName: string;
export let tabLink: string;
const firstTabPage = [
"save",
"settings",
"updates"
];
const firstTabPage = ["save", "settings", "updates"];
let tab: HTMLElement;
$: currentTab = $page.url.pathname.split('/')[1];
$: baseTabPath = tabLink.split('/')[1]
$: currentTab = $page.url.pathname.split("/")[1];
$: baseTabPath = tabLink.split("/")[1];
$: isTabActive = currentTab === baseTabPath;
const showTab = (e: HTMLElement | undefined) => {
if (e) {
e.scrollIntoView({
inline: firstTabPage.includes(tabName) ? 'end' : 'start',
behavior: 'smooth'
inline: firstTabPage.includes(tabName) ? "end" : "start",
behavior: "smooth",
});
}
}
};
$: if (isTabActive) {
showTab(tab)
showTab(tab);
}
</script>
@@ -38,9 +36,10 @@
href={tabLink}
bind:this={tab}
on:focus={() => showTab(tab)}
role="tab"
>
<slot></slot>
{tabName}
{$t(`tabs.${tabName}`)}
</a>
<style>

View File

@@ -4,10 +4,14 @@ const isIOS = ua.includes("iphone os") || (ua.includes("mac os") && navigator.ma
const isAndroid = ua.includes("android") || ua.includes("diordna");
const isMobile = isIOS || isAndroid;
const deviceInfo = {
const preferredLocale = navigator.language.toLowerCase().slice(0, 2);
const device = {
isIOS,
isAndroid,
isMobile,
preferredLocale,
}
export default deviceInfo;
export default device;

View File

@@ -0,0 +1,48 @@
import i18n from 'sveltekit-i18n';
import type { Config } from 'sveltekit-i18n';
import languages from '$i18n/languages.json';
export const defaultLocale = 'en';
export const config: Config = {
translations: {
en: { languages },
ru: { languages },
},
loaders: [
{
locale: 'en',
key: 'tabs',
loader: async () => (
await import(`$i18n/en/tabs.json`)
).default,
},
{
locale: 'en',
key: 'a11y.tabs',
loader: async () => (
await import(`$i18n/en/a11y/tabs.json`)
).default,
},
{
locale: 'ru',
key: 'tabs',
loader: async () => (
await import(`$i18n/ru/tabs.json`)
).default,
},
{
locale: 'ru',
key: 'a11y.tabs',
loader: async () => (
await import(`$i18n/ru/a11y/tabs.json`)
).default,
},
],
};
export const {
t, loading, locales, locale, translations,
loadTranslations, addTranslations, setLocale, setRoute
} = new i18n(config);

View File

@@ -1,18 +1,21 @@
import { defaultLocale } from "$lib/i18n/translations";
import type { CobaltSettings } from "$lib/types/settings";
const defaultSettings: CobaltSettings = {
schemaVersion: 1,
accessibility: {
reduceAnimations: false,
reduceTransparency: false
reduceTransparency: false,
},
appearance: {
theme: "auto"
theme: "auto",
language: defaultLocale,
autoLanguage: true,
},
general: {
customProcessingEndpoint: "",
seenOnboarding: false,
seenSafetyWarning: false
seenSafetyWarning: false,
},
save: {
audioFormat: "mp3",
@@ -25,22 +28,11 @@ const defaultSettings: CobaltSettings = {
twitterGif: false,
videoQuality: "720",
youtubeVideoCodec: "h264",
youtubeDubBrowserLang: false
youtubeDubBrowserLang: false,
},
privacy: {
trafficAnalytics: true
}
trafficAnalytics: true,
},
}
export default defaultSettings;
export const settingArrays = {
appearance: {
theme: ["auto", "light", "dark"]
},
save: {
audioFormat: ["best", "mp3", "ogg", "wav", "opus"],
filenameStyle: ["classic", "basic", "pretty", "nerdy"],
videoQuality: ["max", "2160", "1440", "1080", "720", "480", "360", "240", "144"],
youtubeVideoCodec: ["h264", "av1", "vp9"],
},
}
export default defaultSettings;

View File

@@ -1,3 +1,5 @@
import languages from '$i18n/languages.json';
export type CobaltSettingsAccessibility = {
reduceAnimations: boolean,
reduceTransparency: boolean,
@@ -12,6 +14,8 @@ export const youtubeVideoCodecOptions = ["h264", "av1", "vp9"] as const;
type CobaltSettingsAppearance = {
theme: typeof themeOptions[number],
language: keyof typeof languages,
autoLanguage: boolean,
};
type CobaltSettingsGeneral = {

View File

@@ -1,2 +1,39 @@
export const prerender = true;
export const ssr = false;
import { browser } from '$app/environment';
import { get } from 'svelte/store';
import type { Load } from '@sveltejs/kit';
import languages from '$i18n/languages.json';
import { loadTranslations, defaultLocale } from '$lib/i18n/translations';
import device from '$lib/device.js';
export const load: Load = async ({ url }) => {
const { pathname } = url;
let preferredLocale = defaultLocale;
if (browser) {
const settings = get((await import('$lib/settings')).default);
const deviceLanguage = device.preferredLocale;
const settingsLanguage = settings.appearance.language;
const isValid = (lang: string) => (
Object.keys(languages).includes(lang)
);
if (settings.appearance.autoLanguage) {
if (isValid(deviceLanguage)) {
preferredLocale = deviceLanguage;
}
} else if (isValid(settingsLanguage)) {
preferredLocale = settingsLanguage
}
}
await loadTranslations(preferredLocale, pathname);
return {};
}

View File

@@ -5,6 +5,7 @@
import SettingsToggle from "$components/buttons/SettingsToggle.svelte";
import { themeOptions } from "$lib/types/settings";
import LanguageDropdown from "$components/settings/LanguageDropdown.svelte";
</script>
<SettingsCategory title="theme">
@@ -31,3 +32,13 @@
description="replaces rapid animations with smooth transitions."
/>
</SettingsCategory>
<SettingsCategory title="language">
<LanguageDropdown />
<SettingsToggle
settingContext="appearance"
settingId="autoLanguage"
title="use default browser language"
description="automatically picks the best language for you. if preferred browser language isn't available, english is used instead."
/>
</SettingsCategory>