mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00

* Update to latest Node LTS * Add sveltekit * Split tslib into separate @generated and @tslib components SvelteKit's path aliases don't support multiple locations, so our old approach of using @tslib to refer to both ts/lib and out/ts/lib will no longer work. Instead, all generated sources and their includes are placed in a separate out/ts/generated folder, and imported via @generated instead. This also allows us to generate .ts files, instead of needing to output separate .d.ts and .js files. * Switch package.json to module type * Avoid usage of baseUrl Incompatible with SvelteKit * Move sass into ts; use relative links SvelteKit's default sass support doesn't allow overriding loadPaths * jest->vitest, graphs example working with yarn dev * most pages working in dev mode * Some fixes after rebasing * Fix/silence some svelte-check errors * Get image-occlusion working with Fabric types * Post-rebase lock changes * Editor is now checked * SvelteKit build integrated into ninja * Use the new SvelteKit entrypoint for pages like congrats/deck options/etc * Run eslint once for ts/**; fix some tests * Fix a bunch of issues introduced when rebasing over latest main * Run eslint fix * Fix remaining eslint+pylint issues; tests now all pass * Fix some issues with a clean build * Latest bufbuild no longer requires @__PURE__ hack * Add a few missed dependencies * Add yarn.bat to fix Windows build * Fix pages failing to show when ANKI_API_PORT not defined * Fix svelte-check and vitest on Windows * Set node path in ./yarn * Move svelte-kit output to ts/.svelte-kit Sadly, I couldn't figure out a way to store it in out/ if out/ is a symlink, as it breaks module resolution when SvelteKit is run. * Allow HMR inside Anki * Skip SvelteKit build when HMR is defined * Fix some post-rebase issues I should have done a normal merge instead.
105 lines
2.9 KiB
TypeScript
105 lines
2.9 KiB
TypeScript
// Copyright: Ankitects Pty Ltd and contributors
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
import "intl-pluralrules";
|
|
|
|
import { i18nResources } from "@generated/backend";
|
|
import type { ModuleName } from "@generated/ftl";
|
|
import { FluentBundle, FluentResource } from "@generated/ftl";
|
|
import { firstLanguage, setBundles } from "@generated/ftl";
|
|
|
|
export function supportsVerticalText(): boolean {
|
|
const firstLang = firstLanguage();
|
|
return (
|
|
firstLang.startsWith("ja")
|
|
|| firstLang.startsWith("zh")
|
|
|| firstLang.startsWith("ko")
|
|
);
|
|
}
|
|
|
|
export function direction(): string {
|
|
const firstLang = firstLanguage();
|
|
if (
|
|
firstLang.startsWith("ar")
|
|
|| firstLang.startsWith("he")
|
|
|| firstLang.startsWith("fa")
|
|
) {
|
|
return "rtl";
|
|
} else {
|
|
return "ltr";
|
|
}
|
|
}
|
|
|
|
export function weekdayLabel(n: number): string {
|
|
const firstLang = firstLanguage();
|
|
const now = new Date();
|
|
const daysFromToday = -now.getDay() + n;
|
|
const desiredDay = new Date(now.getTime() + daysFromToday * 86_400_000);
|
|
return desiredDay.toLocaleDateString(firstLang, {
|
|
weekday: "narrow",
|
|
});
|
|
}
|
|
|
|
let langs: string[] = [];
|
|
|
|
export function localizedDate(
|
|
date: Date,
|
|
options?: Intl.DateTimeFormatOptions,
|
|
): string {
|
|
return date.toLocaleDateString(langs, options);
|
|
}
|
|
|
|
export function localizedNumber(n: number, precision = 2): string {
|
|
const round = Math.pow(10, precision);
|
|
const rounded = Math.round(n * round) / round;
|
|
return rounded.toLocaleString(langs);
|
|
}
|
|
|
|
export function localeCompare(
|
|
first: string,
|
|
second: string,
|
|
options?: Intl.CollatorOptions,
|
|
): number {
|
|
return first.localeCompare(second, langs, options);
|
|
}
|
|
|
|
/** Treat text like HTML, merging multiple spaces and converting
|
|
newlines to spaces. */
|
|
export function withCollapsedWhitespace(s: string): string {
|
|
return s.replace(/\s+/g, " ");
|
|
}
|
|
|
|
export function withoutUnicodeIsolation(s: string): string {
|
|
return s.replace(/[\u2068-\u2069]+/g, "");
|
|
}
|
|
|
|
export async function setupI18n(args: { modules: ModuleName[] }): Promise<void> {
|
|
const resources = await i18nResources(args);
|
|
const json = JSON.parse(new TextDecoder().decode(resources.json));
|
|
|
|
const newBundles: FluentBundle[] = [];
|
|
for (const res in json.resources) {
|
|
const text = json.resources[res];
|
|
const lang = json.langs[res];
|
|
const bundle = new FluentBundle([lang, "en-US"]);
|
|
const resource = new FluentResource(text);
|
|
bundle.addResource(resource);
|
|
newBundles.push(bundle);
|
|
}
|
|
|
|
setBundles(newBundles);
|
|
langs = json.langs;
|
|
|
|
document.dir = direction();
|
|
}
|
|
|
|
let globalI18n: Promise<void> | undefined;
|
|
|
|
export async function setupGlobalI18n(): Promise<void> {
|
|
if (!globalI18n) {
|
|
globalI18n = setupI18n({
|
|
modules: [],
|
|
});
|
|
}
|
|
await globalI18n;
|
|
}
|