wayBetter for X

This commit is contained in:
CRTkafa 2026-10-01 06:01:13 +07:00
commit df5dff619e
720 changed files with 147465 additions and 0 deletions

View file

@ -0,0 +1,20 @@
// Vite-only: every catalog becomes its own lazy chunk, loaded when that language is picked. The app's
// messages (`locales/<tag>.json`) are merged over the framework's (`waybetter` kit words).
import { setCatalogLoader } from './index.ts';
type CatalogModule = { messages?: Record<string, string> };
const app = import.meta.glob<CatalogModule>('../../locales/*.json', { import: 'default' });
const framework = import.meta.glob<CatalogModule>('../../../../waybetter/packages/waybetter/locales/*.json', { import: 'default' });
const find = (table: Record<string, () => Promise<CatalogModule>>, tag: string) =>
Object.entries(table).find(([path]) => path.endsWith(`/${tag}.json`))?.[1];
export function registerCatalogs() {
setCatalogLoader(async tag => {
if (tag === 'en') return null;
const [own, kit] = await Promise.all([find(app, tag)?.(), find(framework, tag)?.()]);
if (!own && !kit) return null;
return { ...(kit?.messages ?? {}), ...(own?.messages ?? {}) };
});
}

View file

@ -0,0 +1,39 @@
// The app's i18n instance for the shell, the wayBetter panel and the drafts popover (the X runtime
// has its own, fed natively: src/inject/i18n.ts). The instance and its helpers are the framework's
// (waybetter/packages/shell/src/frame/appI18n.ts): English (`locales/en.json`, plus the framework's
// words) is always loaded; other catalogs load on demand (src/i18n/catalogs.ts registers the Vite
// loader). Components call `useT()` so a language switch re-renders them in place.
//
// Contributors: see docs/TRANSLATING.md. Keys are typed from en.json: `t('common.close')`.
import { useSyncExternalStore } from 'react';
import source from '../../locales/en.json' with { type: 'json' };
import kitSource from '../../../../waybetter/packages/waybetter/locales/en.json' with { type: 'json' };
import languages from '../../locales/languages.json' with { type: 'json' };
import type { Params } from '../../../../waybetter/packages/runtime/src/i18n.ts';
import { createAppI18n, type AppLanguage } from '../../../../waybetter/packages/shell/src/frame/appI18n.ts';
import { AREA_MESSAGES } from '../areas/messages.ts';
import { setCoreText } from '../core/text.ts';
export type MessageKey = keyof typeof source.messages | keyof typeof kitSource.messages;
export type { Params };
export type Language = AppLanguage;
/** Every language with a catalog, English first (generated by `node scripts/i18n-check.mjs --write`). */
export const LANGUAGES = languages as Language[];
/** Keys native code and X tabs need; the shell hands exactly these over (`i18n_set`). */
export const NATIVE_PREFIXES = ['x.', 'kit.', 'plugin.', 'native.'] as const;
const dev = (() => { try { return !!(import.meta as { env?: { DEV?: boolean } }).env?.DEV; } catch { return false; } })();
export const { i18n, setCatalogLoader, t, tx, useT, useLocale, relativeTime, formatDate, formatNumber } = createAppI18n<MessageKey>({
// The areas' new words (src/areas/<area>/messages.en.json) count as English until they are in en.json.
source: { ...kitSource.messages, ...AREA_MESSAGES, ...source.messages },
languages: LANGUAGES,
useSyncExternalStore,
dev,
});
// Shared modules (src/core) speak through this instance in the shell.
setCoreText((key, params) => i18n.tx(key, params));

View file

@ -0,0 +1,26 @@
// Which language the app speaks: the framework's language state (waybetter
// packages/shell/src/frame/language.ts) over the app's i18n instance. The owner picks "Follow X"
// (default: the language X itself is set to, as X tabs report their `<html lang>`), "Follow system"
// or a language; the shell switches every window live and hands native code and X tabs exactly the
// strings they need (`i18n_set`). The drafts popover follows through localStorage.
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { createLanguage, resolveLocale as resolveLocaleCore, type LanguagePreference } from '../../../../waybetter/packages/shell/src/frame/language.ts';
import { i18n, LANGUAGES, NATIVE_PREFIXES } from './index.ts';
export type { LanguagePreference };
/** The supported tier (languages.json `tier`): automatic choices resolve only to these. */
export const SUPPORTED = LANGUAGES.filter(language => language.tier === 'supported').map(language => language.code);
export const language = createLanguage({ i18n, nativePrefixes: NATIVE_PREFIXES, native: { invoke, listen }, supported: SUPPORTED });
export const STORAGE_KEY = language.storageKey;
export const initShellLanguage = language.initShell;
export const initFollowerLanguage = language.initFollower;
export const forcePreviewLanguage = language.forcePreview;
/** The catalog to use for a preference, X's reported language and the OS preferences. */
export const resolveLocale = (preference: LanguagePreference, siteLang: string | null, system: readonly string[]) =>
resolveLocaleCore(preference, siteLang, system, i18n.available, SUPPORTED);