refactor: remove dead code (#3337)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-09 01:41:26 +02:00
1 parent a19002652b
commit 93d1f8af46
1527 files changed
+21170 -73846

No files matched your search

-15
View File
@@ -1,15 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import * as fs from 'node:fs';
import * as path from 'node:path';
export function localeFilePath(locale: string, localesPath: string): string {
return path.join(localesPath, `${locale}.yaml`);
}
export function hasLocaleFile(locale: string, localesPath: string, defaultLocale: string): boolean {
if (locale === defaultLocale) {
return true;
}
return fs.existsSync(localeFilePath(locale, localesPath));
}
-11
View File
@@ -1,11 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {parse as parseYaml} from 'yaml';
export function parseYamlRecord(raw: string): Record<string, unknown> {
const parsed = parseYaml(raw);
if (typeof parsed === 'object' && parsed !== null) {
return parsed as Record<string, unknown>;
}
return {};
}
@@ -1,20 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {I18nConfig} from '@fluxer/i18n/src/runtime/I18nTypes';
export function buildTemplates<TKey extends string, TValue, TVariables>(
record: Record<string, unknown>,
config: I18nConfig<TKey, TValue, TVariables>,
filePath: string,
): Map<TKey, TValue> {
const templates = new Map<TKey, TValue>();
for (const [key, value] of Object.entries(record)) {
const template = config.parseTemplate(value, key);
if (template !== null) {
templates.set(key as TKey, template);
} else {
config.onWarning?.(`Skipping invalid template in ${filePath}: ${key}`);
}
}
return templates;
}
-61
View File
@@ -1,61 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import * as fs from 'node:fs';
import {hasLocaleFile} from '@fluxer/i18n/src/io/LocaleFilePath';
import {parseYamlRecord} from '@fluxer/i18n/src/io/ParseYamlRecord';
import {buildTemplates} from '@fluxer/i18n/src/runtime/BuildTemplates';
import {getTemplate} from '@fluxer/i18n/src/runtime/GetTemplate';
import type {I18nConfig, I18nResult, I18nState, TemplateCompiler} from '@fluxer/i18n/src/runtime/I18nTypes';
interface I18nModule<TKey extends string, TValue, TVariables> {
getTemplate(key: TKey, locale: string | null, variables: TVariables): I18nResult<TKey, TValue>;
hasLocale(locale: string): boolean;
getLoadedLocales(): Set<string>;
reset(): void;
getState(): I18nState<TKey, TValue, TVariables>;
}
export function createI18n<TKey extends string, TValue, TVariables>(
config: I18nConfig<TKey, TValue, TVariables>,
compile: TemplateCompiler<TValue, TVariables>,
): I18nModule<TKey, TValue, TVariables> {
const onWarning = config.onWarning ?? console.warn;
const state: I18nState<TKey, TValue, TVariables> = {
loadedLocales: new Set(),
templatesByLocale: new Map(),
messageFormatCache: new Map(),
config: {...config, onWarning},
};
loadDefaultLocale();
function loadDefaultLocale(): void {
if (!fs.existsSync(state.config.defaultMessagesFile)) {
onWarning(`Default messages bundle not found: ${state.config.defaultMessagesFile}`);
return;
}
const raw = fs.readFileSync(state.config.defaultMessagesFile, 'utf8');
const parsed = parseYamlRecord(raw);
const templates = buildTemplates(parsed, state.config, state.config.defaultMessagesFile);
state.templatesByLocale.set(state.config.defaultLocale, templates);
state.loadedLocales.add(state.config.defaultLocale);
}
return {
getTemplate(key: TKey, locale: string | null, variables: TVariables): I18nResult<TKey, TValue> {
return getTemplate(state, key, locale, variables, compile);
},
hasLocale(locale: string): boolean {
return hasLocaleFile(locale, state.config.localesPath, state.config.defaultLocale);
},
getLoadedLocales(): Set<string> {
return new Set(state.loadedLocales);
},
reset(): void {
state.loadedLocales.clear();
state.templatesByLocale.clear();
state.messageFormatCache.clear();
loadDefaultLocale();
},
getState(): I18nState<TKey, TValue, TVariables> {
return state;
},
};
}
+2 -19
View File
@@ -2,10 +2,10 @@
import type {I18nResult, TemplateCompiler} from '@fluxer/i18n/src/runtime/I18nTypes';
import {renderTemplate} from '@fluxer/i18n/src/runtime/RenderTemplate';
import {getEffectiveStaticLocale, hasStaticLocale} from '@fluxer/i18n/src/runtime/StaticLocale';
import {getEffectiveStaticLocale} from '@fluxer/i18n/src/runtime/StaticLocale';
import type {default as MessageFormat, MessageFormatOptions} from '@messageformat/core';
export type StaticLocaleMessages<TKey extends string, TValue> = Partial<Record<TKey, TValue>>;
type StaticLocaleMessages<TKey extends string, TValue> = Partial<Record<TKey, TValue>>;
export interface StaticI18nConfig<TKey extends string, TValue, TVariables> {
defaultLocale: string;
@@ -19,21 +19,16 @@ export interface StaticI18nConfig<TKey extends string, TValue, TVariables> {
interface StaticI18nModule<TKey extends string, TValue, TVariables> {
getTemplate(key: TKey, locale: string | null, variables: TVariables): I18nResult<TKey, TValue>;
hasLocale(locale: string): boolean;
getLoadedLocales(): Set<string>;
reset(): void;
}
export function createStaticI18n<TKey extends string, TValue, TVariables>(
config: StaticI18nConfig<TKey, TValue, TVariables>,
compile: TemplateCompiler<TValue, TVariables>,
): StaticI18nModule<TKey, TValue, TVariables> {
const loadedLocales = new Set<string>([config.defaultLocale]);
const messageFormatCache = new Map<string, MessageFormat>();
return {
getTemplate(key: TKey, locale: string | null, variables: TVariables): I18nResult<TKey, TValue> {
const effectiveLocale = getEffectiveStaticLocale(config, locale);
loadedLocales.add(effectiveLocale);
const sourceTemplate = Object.hasOwn(config.defaultMessages, key) ? config.defaultMessages[key] : undefined;
if (sourceTemplate === undefined) {
return {
@@ -74,17 +69,5 @@ export function createStaticI18n<TKey extends string, TValue, TVariables>(
messageFormatOptions: config.messageFormatOptions,
});
},
hasLocale(locale: string): boolean {
const normalizedLocale = config.normalizeLocale?.(locale) ?? locale;
return hasStaticLocale(config, normalizedLocale);
},
getLoadedLocales(): Set<string> {
return new Set(loadedLocales);
},
reset(): void {
loadedLocales.clear();
loadedLocales.add(config.defaultLocale);
messageFormatCache.clear();
},
};
}
@@ -1,23 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {hasLocaleFile} from '@fluxer/i18n/src/io/LocaleFilePath';
import type {I18nState} from '@fluxer/i18n/src/runtime/I18nTypes';
export function getEffectiveLocale<TKey extends string, TValue, TVariables>(
state: I18nState<TKey, TValue, TVariables>,
locale: string | null | undefined,
): string {
if (!locale) {
return state.config.defaultLocale;
}
const normalizeLocale = state.config.normalizeLocale ?? ((locale: string) => locale);
const normalizedLocale = normalizeLocale(locale);
if (normalizedLocale === state.config.defaultLocale) {
return state.config.defaultLocale;
}
if (!hasLocaleFile(normalizedLocale, state.config.localesPath, state.config.defaultLocale)) {
state.config.onWarning?.(`Unsupported locale, falling back to ${state.config.defaultLocale}: ${locale}`);
return state.config.defaultLocale;
}
return normalizedLocale;
}
-51
View File
@@ -1,51 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {getEffectiveLocale} from '@fluxer/i18n/src/runtime/GetEffectiveLocale';
import type {I18nResult, I18nState, TemplateCompiler} from '@fluxer/i18n/src/runtime/I18nTypes';
import {loadLocaleIfNotLoaded} from '@fluxer/i18n/src/runtime/LoadLocale';
import {renderTemplate} from '@fluxer/i18n/src/runtime/RenderTemplate';
export function getTemplate<TKey extends string, TValue, TVariables>(
state: I18nState<TKey, TValue, TVariables>,
key: TKey,
locale: string | null,
variables: TVariables,
compile: TemplateCompiler<TValue, TVariables>,
): I18nResult<TKey, TValue> {
const effectiveLocale = getEffectiveLocale(state, locale);
loadLocaleIfNotLoaded(state, effectiveLocale);
const sourceTemplate = state.templatesByLocale.get(state.config.defaultLocale)?.get(key);
if (!sourceTemplate) {
return {
ok: false,
error: {
kind: 'missing-template',
key,
message: `Missing template ${key}`,
},
locale: effectiveLocale,
};
}
const translatedTemplate = state.templatesByLocale.get(effectiveLocale)?.get(key);
const template = translatedTemplate ?? sourceTemplate;
const validationError = state.config.validateVariables?.(key, template, variables);
if (validationError) {
return {
ok: false,
error: {
kind: 'invalid-variables',
key,
message: validationError,
},
locale: effectiveLocale,
};
}
return renderTemplate({
key,
locale: effectiveLocale,
template,
variables,
compile,
messageFormatCache: state.messageFormatCache,
});
}
+1 -18
View File
@@ -4,7 +4,7 @@ import type MessageFormat from '@messageformat/core';
type I18nErrorKind = 'missing-template' | 'invalid-variables' | 'compile-failed';
export interface I18nError<TKey extends string> {
interface I18nError<TKey extends string> {
kind: I18nErrorKind;
key: TKey;
message: string;
@@ -22,23 +22,6 @@ export type I18nResult<TKey extends string, TValue> =
locale: string;
};
export interface I18nState<TKey extends string, TValue, TVariables> {
loadedLocales: Set<string>;
templatesByLocale: Map<string, Map<TKey, TValue>>;
messageFormatCache: Map<string, MessageFormat>;
config: I18nConfig<TKey, TValue, TVariables>;
}
export interface I18nConfig<TKey extends string, TValue, TVariables> {
localesPath: string;
defaultLocale: string;
defaultMessagesFile: string;
normalizeLocale?: (locale: string) => string;
parseTemplate: (value: unknown, key: string) => TValue | null;
onWarning?: (message: string) => void;
validateVariables?: (key: TKey, template: TValue, variables: TVariables) => string | null;
}
export type TemplateCompiler<TValue, TVariables> = (
template: TValue,
variables: TVariables,
-38
View File
@@ -1,38 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import * as fs from 'node:fs';
import {localeFilePath} from '@fluxer/i18n/src/io/LocaleFilePath';
import {parseYamlRecord} from '@fluxer/i18n/src/io/ParseYamlRecord';
import {buildTemplates} from '@fluxer/i18n/src/runtime/BuildTemplates';
import type {I18nState} from '@fluxer/i18n/src/runtime/I18nTypes';
export function loadLocaleIfNotLoaded<TKey extends string, TValue, TVariables>(
state: I18nState<TKey, TValue, TVariables>,
locale: string,
): void {
if (locale === state.config.defaultLocale) {
return;
}
if (state.loadedLocales.has(locale)) {
return;
}
const filePath = localeFilePath(locale, state.config.localesPath);
if (!fs.existsSync(filePath)) {
state.config.onWarning?.(
`Locale file not found for ${locale}: ${filePath}. Falling back to ${state.config.defaultLocale}.`,
);
return;
}
const raw = fs.readFileSync(filePath, 'utf8');
const parsed = parseYamlRecord(raw);
const templates = buildTemplates(parsed, state.config, filePath);
let localeMap = state.templatesByLocale.get(locale);
if (!localeMap) {
localeMap = new Map();
state.templatesByLocale.set(locale, localeMap);
}
for (const [key, template] of templates) {
localeMap.set(key, template);
}
state.loadedLocales.add(locale);
}
@@ -2,7 +2,7 @@
import {parse, type Token} from '@messageformat/parser';
export type MessageVariableValue = string | number | boolean | Date;
type MessageVariableValue = string | number | boolean | Date;
type Whitespace = ' ' | '\n' | '\r' | '\t';
type TrimLeft<T extends string> = T extends `${Whitespace}${infer Rest}` ? TrimLeft<Rest> : T;
type TrimRight<T extends string> = T extends `${infer Rest}${Whitespace}` ? TrimRight<Rest> : T;
@@ -31,10 +31,10 @@ type NumericPlaceholderNameAfterOpen<T extends string> = T extends `${infer Name
: T extends `${infer Name}, selectordinal,${string}`
? PlaceholderName<Name>
: never;
export type TemplatePlaceholderNames<T extends string> = T extends `${string}{${infer AfterOpen}`
type TemplatePlaceholderNames<T extends string> = T extends `${string}{${infer AfterOpen}`
? PlaceholderNameAfterOpen<AfterOpen> | TemplatePlaceholderNames<RestAfterPlaceholder<AfterOpen>>
: never;
export type NumericTemplatePlaceholderNames<T extends string> = T extends `${string}{${infer AfterOpen}`
type NumericTemplatePlaceholderNames<T extends string> = T extends `${string}{${infer AfterOpen}`
? NumericPlaceholderNameAfterOpen<AfterOpen> | NumericTemplatePlaceholderNames<RestAfterPlaceholder<AfterOpen>>
: never;
export type MessageVariablesForTemplate<T extends string> = [TemplatePlaceholderNames<T>] extends [never]
@@ -47,9 +47,6 @@ export type MessageVariablesForTemplate<T extends string> = [TemplatePlaceholder
export type MessageArgsForTemplate<T extends string> = [MessageVariablesForTemplate<T>] extends [never]
? [variables?: undefined]
: [variables: MessageVariablesForTemplate<T>];
export type MessageArgsWithFallbackForTemplate<T extends string> = [MessageVariablesForTemplate<T>] extends [never]
? [variables?: undefined, fallbackMessage?: string]
: [variables: MessageVariablesForTemplate<T>, fallbackMessage?: string];
type LocaleMessageForTemplate<Source extends string, Translation extends string> = [
Exclude<TemplatePlaceholderNames<Source>, TemplatePlaceholderNames<Translation>>,
] extends [never]
+1 -1
View File
@@ -2,7 +2,7 @@
import type {StaticI18nConfig} from '@fluxer/i18n/src/runtime/CreateStaticI18n';
export function hasStaticLocale<TKey extends string, TValue, TVariables>(
function hasStaticLocale<TKey extends string, TValue, TVariables>(
config: StaticI18nConfig<TKey, TValue, TVariables>,
normalizedLocale: string,
): boolean {
@@ -79,9 +79,6 @@ describe('compileMessage', () => {
expect(unwrap(i18n.getTemplate('greeting', 'en-US', {name: 'Taylor'}))).toBe('Hello Taylor');
expect(unwrap(i18n.getTemplate('greeting', 'en-US', {name: 'Jordan'}))).toBe('Hello Jordan');
expect(compileSpy).toHaveBeenCalledTimes(3);
i18n.reset();
expect(unwrap(i18n.getTemplate('files', 'en-US', {count: 3}))).toBe('3 files other');
expect(compileSpy).toHaveBeenCalledTimes(4);
});
});
@@ -1,144 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import {createI18n} from '@fluxer/i18n/src/runtime/CreateI18n';
import type {I18nResult} from '@fluxer/i18n/src/runtime/I18nTypes';
import {beforeEach, describe, expect, it} from 'vitest';
type TestKey = 'greeting' | 'farewell' | 'with_vars';
function writeFile(filePath: string, content: string): void {
fs.mkdirSync(path.dirname(filePath), {recursive: true});
fs.writeFileSync(filePath, content, 'utf8');
}
function unwrapResult(result: I18nResult<TestKey, string>): string {
expect(result.ok).toBe(true);
if (result.ok) {
return result.value;
}
throw new Error(result.error.message);
}
describe('createI18n', () => {
let tempDir: string;
let localesPath: string;
let messagesFile: string;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fluxer-i18n-'));
localesPath = path.join(tempDir, 'locales');
messagesFile = path.join(localesPath, 'messages.yaml');
writeFile(
messagesFile,
["'farewell': 'Bye'", "'greeting': 'Hello {name}'", "'with_vars': 'Hello {name}, you have {count}'", ''].join(
'\n',
),
);
writeFile(path.join(localesPath, 'fr.yaml'), ["'greeting': 'Salut {name}'", ''].join('\n'));
});
it('returns compiled templates from the default locale', () => {
const i18n = createI18n<TestKey, string, Record<string, unknown>>(
{
localesPath,
defaultLocale: 'en-US',
defaultMessagesFile: messagesFile,
parseTemplate: (value) => (typeof value === 'string' ? value : null),
},
(template, variables, mf) => String(mf.compile(template)(variables)),
);
const result = i18n.getTemplate('greeting', 'en-US', {name: 'Taylor'});
expect(unwrapResult(result)).toBe('Hello Taylor');
});
it('loads locale files on demand', () => {
const i18n = createI18n<TestKey, string, Record<string, unknown>>(
{
localesPath,
defaultLocale: 'en-US',
defaultMessagesFile: messagesFile,
parseTemplate: (value) => (typeof value === 'string' ? value : null),
},
(template, variables, mf) => String(mf.compile(template)(variables)),
);
const result = i18n.getTemplate('greeting', 'fr', {name: 'Taylor'});
expect(unwrapResult(result)).toBe('Salut Taylor');
});
it('returns a missing-template error result', () => {
const i18n = createI18n<TestKey, string, Record<string, unknown>>(
{
localesPath,
defaultLocale: 'en-US',
defaultMessagesFile: messagesFile,
parseTemplate: (value) => (typeof value === 'string' ? value : null),
},
(template, variables, mf) => String(mf.compile(template)(variables)),
);
const result = i18n.getTemplate('missing' as TestKey, 'en-US', {name: 'Taylor'});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error.kind).toBe('missing-template');
}
});
it('returns invalid-variables error when validation fails', () => {
const i18n = createI18n<TestKey, string, Record<string, unknown> | undefined>(
{
localesPath,
defaultLocale: 'en-US',
defaultMessagesFile: messagesFile,
parseTemplate: (value) => (typeof value === 'string' ? value : null),
validateVariables: (key, template, variables) => {
if (variables) {
return null;
}
if (template.includes('{')) {
return `Missing variables for ${key}`;
}
return null;
},
},
(template, variables, mf) => String(mf.compile(template)(variables ?? {})),
);
const result = i18n.getTemplate('with_vars', 'en-US', undefined);
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error.kind).toBe('invalid-variables');
}
});
it('returns compile-failed when the compiler throws', () => {
const i18n = createI18n<TestKey, string, Record<string, unknown>>(
{
localesPath,
defaultLocale: 'en-US',
defaultMessagesFile: messagesFile,
parseTemplate: (value) => (typeof value === 'string' ? value : null),
},
() => {
throw new Error('compile failed');
},
);
const result = i18n.getTemplate('greeting', 'en-US', {name: 'Taylor'});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error.kind).toBe('compile-failed');
}
});
it('returns default locale for unsupported locales', () => {
const warnings: Array<string> = [];
const i18n = createI18n<TestKey, string, Record<string, unknown>>(
{
localesPath,
defaultLocale: 'en-US',
defaultMessagesFile: messagesFile,
parseTemplate: (value) => (typeof value === 'string' ? value : null),
onWarning: (message) => {
warnings.push(message);
},
},
(template, variables, mf) => String(mf.compile(template)(variables)),
);
const result = i18n.getTemplate('greeting', 'pt-BR', {name: 'Taylor'});
expect(unwrapResult(result)).toBe('Hello Taylor');
expect(warnings).toContain('Unsupported locale, falling back to en-US: pt-BR');
});
});