mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
feat: server side strings
This commit is contained in:
1 parent
bdabadcd49
commit
44203223fd
9 files changed
+76
-105
No files matched your search
@@ -1,4 +1,2 @@
|
|||||||
export * from "./literals";
|
export * from "./literals";
|
||||||
export * from "./strings";
|
export * from "./strings";
|
||||||
export * from "./unwrapper";
|
|
||||||
export * from "./wrapper";
|
|
||||||
@@ -1,68 +1,3 @@
|
|||||||
import { merge } from "ts-deepmerge";
|
import type en from "../strings/en.json";
|
||||||
import locales from "../locales.json";
|
|
||||||
import en from "../strings/en.json";
|
|
||||||
import es from "../strings/es.json";
|
|
||||||
import fr from "../strings/fr.json";
|
|
||||||
import ja from "../strings/ja.json";
|
|
||||||
import pl from "../strings/pl.json";
|
|
||||||
import pt from "../strings/pt.json";
|
|
||||||
import ru from "../strings/ru.json";
|
|
||||||
import uk from "../strings/uk.json";
|
|
||||||
import zh from "../strings/zh.json";
|
|
||||||
|
|
||||||
export type BlitzKitStrings = typeof en;
|
export type Strings = typeof en;
|
||||||
|
|
||||||
const stringsRaw: Record<string, TranslationTree> = {
|
|
||||||
en,
|
|
||||||
es,
|
|
||||||
fr,
|
|
||||||
ja,
|
|
||||||
pl,
|
|
||||||
pt,
|
|
||||||
ru,
|
|
||||||
uk,
|
|
||||||
zh,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const STRINGS: Record<string, BlitzKitStrings> = {};
|
|
||||||
|
|
||||||
for (const locale in stringsRaw) {
|
|
||||||
if (locale === locales.default) {
|
|
||||||
STRINGS[locales.default] = stringsRaw[locales.default] as BlitzKitStrings;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localeStrings = stringsRaw[locale];
|
|
||||||
STRINGS[locale] = merge(
|
|
||||||
stringsRaw[locales.default],
|
|
||||||
localeStrings
|
|
||||||
) as BlitzKitStrings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<string, string> = {
|
|
||||||
en: "en",
|
|
||||||
es: "es",
|
|
||||||
fr: "fr",
|
|
||||||
ja: "ja",
|
|
||||||
pt: "pt",
|
|
||||||
ru: "ru",
|
|
||||||
uk: "uk",
|
|
||||||
zh: "zh-Hans",
|
|
||||||
pl: "pl",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SUPPORTED_LOCALE_FLAGS: Record<string, string> = {
|
|
||||||
en: "🇺🇸",
|
|
||||||
es: "🇪🇸",
|
|
||||||
fr: "🇫🇷",
|
|
||||||
ja: "🇯🇵",
|
|
||||||
pt: "🇵🇹",
|
|
||||||
ru: "🇷🇺",
|
|
||||||
uk: "🇺🇦",
|
|
||||||
zh: "🇨🇳",
|
|
||||||
pl: "🇵🇱",
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TranslationTree = { [key: string]: TranslationNode };
|
|
||||||
|
|
||||||
export type TranslationNode = string | TranslationTree;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { I18nString } from "../../core/src";
|
|
||||||
import locales from "../locales.json";
|
|
||||||
|
|
||||||
export function unwrapper(locale = locales.default) {
|
|
||||||
return function (i18nString: I18nString) {
|
|
||||||
if (locale in i18nString.locales) {
|
|
||||||
return i18nString.locales[locale];
|
|
||||||
}
|
|
||||||
|
|
||||||
return i18nString.locales[locales.default];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import locales from "../locales.json";
|
|
||||||
import { type BlitzKitStrings, STRINGS } from "./strings";
|
|
||||||
|
|
||||||
export function wrapper(slicer: (strings: BlitzKitStrings) => string) {
|
|
||||||
const defaultString = slicer(STRINGS[locales.default] as BlitzKitStrings);
|
|
||||||
|
|
||||||
const object = { [locales.default]: defaultString };
|
|
||||||
|
|
||||||
for (const { locale } of locales.supported) {
|
|
||||||
const string = slicer(STRINGS[locale] as BlitzKitStrings);
|
|
||||||
if (string !== defaultString) object[locale] = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"name": "BlitzKit"
|
"name": "BlitzKit",
|
||||||
|
"credit": "by TrèsAbhi"
|
||||||
},
|
},
|
||||||
|
|
||||||
"units": {
|
"units": {
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
||||||
|
import { api } from "../core/api/dynamic";
|
||||||
import { BlitzKitIcon } from "../icons/BlitzKit";
|
import { BlitzKitIcon } from "../icons/BlitzKit";
|
||||||
import { Code } from "./Code";
|
import { Code } from "./Code";
|
||||||
import { TextField } from "./TextField";
|
import { TextField } from "./TextField";
|
||||||
|
|
||||||
const strings = { home: { search: "test" } };
|
interface Props {
|
||||||
|
locale: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale } = Astro.props;
|
||||||
|
|
||||||
|
const strings = await api.strings(locale);
|
||||||
---
|
---
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -39,7 +46,10 @@ const strings = { home: { search: "test" } };
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
background: linear-gradient(transparent, var(--gray-1));
|
background: linear-gradient(
|
||||||
|
color-mix(in srgb, transparent 50%, var(--gray-1)),
|
||||||
|
var(--gray-1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,42 @@
|
|||||||
import type { CatalogItemAccessor } from "@blitzkit/closed";
|
import type { CatalogItemAccessor } from "@blitzkit/closed";
|
||||||
|
import type { Strings } from "@blitzkit/i18n";
|
||||||
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
|
import type { Tank } from "../../protos/tank";
|
||||||
|
import type { TankList } from "../../protos/tank_list";
|
||||||
|
import type { Tanks } from "../../protos/tanks";
|
||||||
|
|
||||||
export abstract class AbstractAPI {
|
export abstract class AbstractAPI {
|
||||||
abstract tanks(): Promise<Tanks>;
|
abstract tanks(): Promise<Tanks>;
|
||||||
|
|
||||||
abstract tankList(): Promise<TankList>;
|
abstract tankList(): Promise<TankList>;
|
||||||
|
|
||||||
abstract strings(
|
abstract _strings(locale: string): Promise<Strings>;
|
||||||
|
abstract _gameStrings(
|
||||||
locale: string,
|
locale: string,
|
||||||
prefix?: string
|
prefix: string
|
||||||
): Promise<Record<string, string>>;
|
): Promise<Record<string, string>>;
|
||||||
|
|
||||||
|
private assertLocale(locale: string) {
|
||||||
|
const supported = locales.supported.find(
|
||||||
|
(supported) => supported.locale === locale
|
||||||
|
);
|
||||||
|
|
||||||
|
if (supported === undefined) {
|
||||||
|
throw new Error(`Unsupported locale: ${locale}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
async strings(locale: string) {
|
||||||
|
this.assertLocale(locale);
|
||||||
|
return await this._strings(locale);
|
||||||
|
}
|
||||||
|
async gameStrings(locale: string, prefix: string) {
|
||||||
|
const supported = this.assertLocale(locale);
|
||||||
|
const blitz = supported.blitz ?? supported.locale;
|
||||||
|
return await this._gameStrings(blitz, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
abstract tank(id: string): Promise<Tank>;
|
abstract tank(id: string): Promise<Tank>;
|
||||||
|
|
||||||
abstract avatars(): Promise<CatalogItemAccessor[]>;
|
abstract avatars(): Promise<CatalogItemAccessor[]>;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import type { MetadataAccessor } from "@blitzkit/closed";
|
import type { MetadataAccessor } from "@blitzkit/closed";
|
||||||
import { sluggify } from "@blitzkit/core";
|
import { sluggify, type DeepPartial } from "@blitzkit/core";
|
||||||
|
import type { Strings } from "@blitzkit/i18n";
|
||||||
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage_component";
|
import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage_component";
|
||||||
|
import { merge } from "lodash-es";
|
||||||
import { parse } from "yaml";
|
import { parse } from "yaml";
|
||||||
import type { Tank } from "../../protos/tank";
|
import type { Tank } from "../../protos/tank";
|
||||||
import type { TankList, TankListEntry } from "../../protos/tank_list";
|
import type { TankList, TankListEntry } from "../../protos/tank_list";
|
||||||
@@ -11,6 +14,8 @@ if (typeof window !== "undefined") {
|
|||||||
throw new Error("ServerAPI is being evaluated in the browser");
|
throw new Error("ServerAPI is being evaluated in the browser");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const globbedStrings = import.meta.glob("../../../../i18n/strings/*.json");
|
||||||
|
|
||||||
interface LocalizationConfig {
|
interface LocalizationConfig {
|
||||||
namespaces: string[];
|
namespaces: string[];
|
||||||
}
|
}
|
||||||
@@ -20,7 +25,7 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async strings(locale: string) {
|
async _gameStrings(locale: string, prefix: string) {
|
||||||
const group = this.metadata.group("ClientConfigsEntity");
|
const group = this.metadata.group("ClientConfigsEntity");
|
||||||
|
|
||||||
if (group.length !== 1) {
|
if (group.length !== 1) {
|
||||||
@@ -89,6 +94,26 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filtered: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (const key in strings) {
|
||||||
|
if (key.startsWith(`${prefix}__`)) {
|
||||||
|
filtered[key] = strings[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _strings(locale: string) {
|
||||||
|
const localized = (await globbedStrings[
|
||||||
|
`../../../../i18n/strings/${locale}.json`
|
||||||
|
]()) as DeepPartial<Strings>;
|
||||||
|
const defaults = (await globbedStrings[
|
||||||
|
`../../../../i18n/strings/${locales.default}.json`
|
||||||
|
]()) as Strings;
|
||||||
|
const strings = merge({}, defaults, localized);
|
||||||
|
|
||||||
return strings;
|
return strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +122,7 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
if (this._tankList === undefined) {
|
if (this._tankList === undefined) {
|
||||||
const group = this.metadata.group("TankEntity");
|
const group = this.metadata.group("TankEntity");
|
||||||
const list: TankListEntry[] = [];
|
const list: TankListEntry[] = [];
|
||||||
const strings = await this.strings("en");
|
const strings = await this.gameStrings("en", "TankEntity");
|
||||||
|
|
||||||
for (const item of group) {
|
for (const item of group) {
|
||||||
const tankCatalog = item.TankCatalog();
|
const tankCatalog = item.TankCatalog();
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ export const getStaticPaths = (() => {
|
|||||||
{ params: { locale: undefined }, props: { locale: locales.default } },
|
{ params: { locale: undefined }, props: { locale: locales.default } },
|
||||||
];
|
];
|
||||||
}) satisfies GetStaticPaths;
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
|
const { locale } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout accent="amber">
|
<BaseLayout accent="amber">
|
||||||
<HomeHero />
|
<HomeHero {locale} />
|
||||||
|
|
||||||
<h1>blitzkit {packageJson.version}</h1>
|
<h1>blitzkit {packageJson.version}</h1>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user