@better-i18n/next 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-i18n/next",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "description": "Better-i18n Next.js integration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -57,7 +57,7 @@
57
57
  "typecheck": "tsc --noEmit"
58
58
  },
59
59
  "dependencies": {
60
- "@better-i18n/core": "workspace:*"
60
+ "@better-i18n/core": "0.1.2"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "next": ">=15.0.0",
@@ -68,7 +68,7 @@
68
68
  "devDependencies": {
69
69
  "next": "^15.4.4",
70
70
  "next-intl": "^4.5.8",
71
- "@better-i18n/typescript-config": "workspace:*",
71
+ "@better-i18n/typescript-config": "0.0.0",
72
72
  "@types/node": "^20.0.0",
73
73
  "@types/react": "^19.0.0",
74
74
  "typescript": "~5.9.2"
package/src/client.ts CHANGED
@@ -1,15 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useMemo, useState } from "react";
4
+ import { createI18nCore } from "@better-i18n/core";
5
+ import type { LanguageOption } from "@better-i18n/core";
4
6
 
5
- import { getProjectBaseUrl, normalizeConfig } from "./config";
6
- import { createLogger } from "./logger";
7
- import { extractLanguages } from "./manifest";
8
- import type {
9
- I18nConfig,
10
- LanguageOption,
11
- ManifestResponse,
12
- } from "./types";
7
+ import { normalizeConfig } from "./config";
8
+ import type { I18nConfig } from "./types";
13
9
 
14
10
  export type UseManifestLanguagesResult = {
15
11
  languages: LanguageOption[];
@@ -23,116 +19,111 @@ type ClientCacheEntry = {
23
19
  promise?: Promise<LanguageOption[]>;
24
20
  };
25
21
 
22
+ // Client-side request deduplication cache
26
23
  const clientCache = new Map<string, ClientCacheEntry>();
27
24
 
28
- const getCacheKey = (config: I18nConfig) =>
29
- `${config.cdnBaseUrl || "https://cdn.better-i18n.com"}|${config.project}`;
30
-
31
- const fetchManifestLanguages = async (
32
- config: I18nConfig,
33
- logger: ReturnType<typeof createLogger>,
34
- signal?: AbortSignal,
35
- ): Promise<LanguageOption[]> => {
36
- const normalized = normalizeConfig(config);
37
- const url = `${getProjectBaseUrl(normalized)}/manifest.json`;
38
- logger.debug("fetch", url);
39
-
40
- const response = await (normalized.fetch ?? fetch)(url, {
41
- cache: "no-store",
42
- signal,
43
- });
44
-
45
- if (!response.ok) {
46
- throw new Error(`[better-i18n] Manifest fetch failed (${response.status})`);
47
- }
48
-
49
- const data = (await response.json()) as ManifestResponse;
50
- const languages = extractLanguages(data);
51
-
52
- if (languages.length === 0) {
53
- throw new Error("[better-i18n] No languages found in manifest");
54
- }
55
-
56
- return languages;
57
- };
58
-
59
- export const useManifestLanguages = (
60
- config: I18nConfig,
61
- ): UseManifestLanguagesResult => {
25
+ const getCacheKey = (project: string, cdnBaseUrl?: string) =>
26
+ `${cdnBaseUrl || "https://cdn.better-i18n.com"}|${project}`;
27
+
28
+ /**
29
+ * React hook to fetch manifest languages on the client
30
+ *
31
+ * Uses `createI18nCore` from `@better-i18n/core` internally with
32
+ * request deduplication to prevent duplicate fetches.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * const { languages, isLoading, error } = useManifestLanguages({
37
+ * project: 'acme/dashboard',
38
+ * defaultLocale: 'en',
39
+ * })
40
+ *
41
+ * if (isLoading) return <Spinner />
42
+ * if (error) return <Error message={error.message} />
43
+ *
44
+ * return (
45
+ * <select>
46
+ * {languages.map(lang => (
47
+ * <option key={lang.code} value={lang.code}>
48
+ * {lang.nativeName || lang.name || lang.code}
49
+ * </option>
50
+ * ))}
51
+ * </select>
52
+ * )
53
+ * ```
54
+ */
55
+ export const useManifestLanguages = (config: I18nConfig): UseManifestLanguagesResult => {
62
56
  const normalized = useMemo(
63
57
  () => normalizeConfig(config),
64
58
  [
65
59
  config.project,
66
60
  config.defaultLocale,
67
61
  config.cdnBaseUrl,
68
- config.localePrefix,
69
62
  config.debug,
70
63
  config.logLevel,
71
- config.manifestCacheTtlMs,
72
- config.manifestRevalidateSeconds,
73
- config.messagesRevalidateSeconds,
74
- config.fetch,
75
64
  ],
76
65
  );
77
- const logger = useMemo(() => createLogger(normalized, "client"), [
78
- normalized,
79
- ]);
80
66
 
81
- const cacheKey = getCacheKey(normalized);
67
+ const i18nCore = useMemo(
68
+ () =>
69
+ createI18nCore({
70
+ project: normalized.project,
71
+ defaultLocale: normalized.defaultLocale,
72
+ cdnBaseUrl: normalized.cdnBaseUrl,
73
+ debug: normalized.debug,
74
+ logLevel: normalized.logLevel,
75
+ }),
76
+ [normalized],
77
+ );
78
+
79
+ const cacheKey = getCacheKey(normalized.project, normalized.cdnBaseUrl);
82
80
  const cached = clientCache.get(cacheKey);
83
81
 
84
- const [languages, setLanguages] = useState<LanguageOption[]>(
85
- cached?.data ?? [],
86
- );
82
+ const [languages, setLanguages] = useState<LanguageOption[]>(cached?.data ?? []);
87
83
  const [isLoading, setIsLoading] = useState(!cached?.data);
88
84
  const [error, setError] = useState<Error | null>(cached?.error ?? null);
89
85
 
90
86
  useEffect(() => {
91
87
  let isMounted = true;
92
- const controller = new AbortController();
93
88
 
94
89
  const run = async () => {
95
- try {
96
- const entry = clientCache.get(cacheKey) ?? {};
97
-
98
- if (entry.data) {
99
- if (isMounted) {
100
- setLanguages(entry.data);
101
- setError(entry.error ?? null);
102
- setIsLoading(false);
103
- }
104
- return;
105
- }
90
+ const entry = clientCache.get(cacheKey) ?? {};
106
91
 
107
- if (!entry.promise) {
108
- entry.promise = fetchManifestLanguages(
109
- normalized,
110
- logger,
111
- controller.signal,
112
- )
113
- .then((nextLanguages) => {
114
- clientCache.set(cacheKey, { data: nextLanguages });
115
- return nextLanguages;
116
- })
117
- .catch((err) => {
118
- const nextError = err instanceof Error ? err : new Error(String(err));
119
- clientCache.set(cacheKey, { error: nextError });
120
- throw nextError;
121
- });
122
- clientCache.set(cacheKey, entry);
92
+ // Return cached data if available
93
+ if (entry.data) {
94
+ if (isMounted) {
95
+ setLanguages(entry.data);
96
+ setIsLoading(false);
123
97
  }
98
+ return;
99
+ }
124
100
 
125
- const nextLanguages = await entry.promise;
101
+ // Deduplicate in-flight requests
102
+ if (!entry.promise) {
103
+ entry.promise = i18nCore
104
+ .getLanguages()
105
+ .then((langs) => {
106
+ clientCache.set(cacheKey, { data: langs });
107
+ return langs;
108
+ })
109
+ .catch((err) => {
110
+ const error = err instanceof Error ? err : new Error(String(err));
111
+ clientCache.set(cacheKey, { error });
112
+ throw error;
113
+ });
114
+ clientCache.set(cacheKey, entry);
115
+ }
126
116
 
117
+ try {
118
+ const langs = await entry.promise;
127
119
  if (isMounted) {
128
- setLanguages(nextLanguages);
120
+ setLanguages(langs);
129
121
  setError(null);
130
122
  }
131
123
  } catch (err) {
132
- if (!isMounted) return;
133
- const nextError = err instanceof Error ? err : new Error(String(err));
134
- logger.error(nextError);
135
- setError(nextError);
124
+ if (isMounted) {
125
+ setError(err instanceof Error ? err : new Error(String(err)));
126
+ }
136
127
  } finally {
137
128
  if (isMounted) {
138
129
  setIsLoading(false);
@@ -144,9 +135,8 @@ export const useManifestLanguages = (
144
135
 
145
136
  return () => {
146
137
  isMounted = false;
147
- controller.abort();
148
138
  };
149
- }, [cacheKey, logger, normalized]);
139
+ }, [cacheKey, i18nCore]);
150
140
 
151
141
  return { languages, isLoading, error };
152
142
  };
package/src/index.ts CHANGED
@@ -1,28 +1,61 @@
1
1
  import type { I18nConfig } from "./types";
2
2
  import { normalizeConfig } from "./config";
3
3
  import {
4
+ createNextI18nCore,
4
5
  createNextIntlRequestConfig,
5
- getLocales,
6
- getManifest,
7
- getMessages,
8
6
  } from "./server";
9
- import { createI18nMiddleware, createI18nProxy } from "./middleware";
7
+ import {
8
+ createI18nMiddleware,
9
+ createI18nProxy,
10
+ createBetterI18nMiddleware,
11
+ composeMiddleware,
12
+ } from "./middleware";
10
13
 
14
+ /**
15
+ * Create a complete i18n setup for Next.js
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * // i18n/config.ts
20
+ * import { createI18n } from '@better-i18n/next'
21
+ *
22
+ * export const i18n = createI18n({
23
+ * project: 'acme/dashboard',
24
+ * defaultLocale: 'en',
25
+ * })
26
+ *
27
+ * // i18n/request.ts
28
+ * export default i18n.requestConfig
29
+ *
30
+ * // middleware.ts
31
+ * export default i18n.middleware
32
+ * ```
33
+ */
11
34
  export const createI18n = (config: I18nConfig) => {
12
35
  const normalized = normalizeConfig(config);
36
+ const i18n = createNextI18nCore(normalized);
13
37
 
14
38
  return {
15
39
  config: normalized,
16
40
  requestConfig: createNextIntlRequestConfig(normalized),
17
41
  middleware: createI18nMiddleware(normalized),
18
42
  proxy: createI18nProxy(normalized),
19
- getManifest: (options?: { forceRefresh?: boolean }) =>
20
- getManifest(normalized, options),
21
- getLocales: () => getLocales(normalized),
22
- getMessages: (locale: string) => getMessages(normalized, locale),
43
+ getManifest: i18n.getManifest,
44
+ getLocales: i18n.getLocales,
45
+ getMessages: i18n.getMessages,
23
46
  };
24
47
  };
25
48
 
49
+ // Modern standalone middleware exports
50
+ export { createBetterI18nMiddleware, composeMiddleware };
51
+
52
+ // Core instance factory
53
+ export { createNextI18nCore } from "./server";
54
+
55
+ // Client hook
56
+ export { useManifestLanguages } from "./client";
57
+
58
+ // Re-export types
26
59
  export type {
27
60
  I18nConfig,
28
61
  LanguageOption,
@@ -33,3 +66,5 @@ export type {
33
66
  ManifestResponse,
34
67
  Messages,
35
68
  } from "./types";
69
+
70
+ export type { I18nMiddlewareConfig } from "@better-i18n/core";
package/src/middleware.ts CHANGED
@@ -1,13 +1,10 @@
1
1
  import createMiddleware from "next-intl/middleware";
2
2
  import { NextRequest, NextResponse } from "next/server";
3
- // @ts-ignore - internal workspace dependency
4
- import { detectLocale, getLocales as getCDNLocales } from "@better-i18n/core";
5
- // @ts-ignore - internal workspace dependency
3
+ import { createI18nCore, createLogger, detectLocale } from "@better-i18n/core";
6
4
  import type { I18nMiddlewareConfig } from "@better-i18n/core";
7
5
 
8
6
  import { normalizeConfig } from "./config";
9
- import { createLogger } from "./logger";
10
- import { getLocales } from "./core";
7
+ import { getLocales } from "./server";
11
8
  import type { I18nConfig } from "./types";
12
9
 
13
10
  /**
@@ -46,9 +43,12 @@ export function createBetterI18nMiddleware(config: I18nMiddlewareConfig) {
46
43
  cookieMaxAge = 31536000,
47
44
  } = detection;
48
45
 
46
+ // Create i18n core instance for CDN operations
47
+ const i18nCore = createI18nCore({ project, defaultLocale });
48
+
49
49
  return async (request: NextRequest): Promise<NextResponse> => {
50
50
  // 1. Fetch available locales from CDN
51
- const availableLocales = await getCDNLocales({ project });
51
+ const availableLocales = await i18nCore.getLocales();
52
52
 
53
53
  // 2. Extract locale indicators
54
54
  const pathLocale = request.nextUrl.pathname.split("/")[1];
@@ -67,8 +67,12 @@ export function createBetterI18nMiddleware(config: I18nMiddlewareConfig) {
67
67
  availableLocales,
68
68
  });
69
69
 
70
- // 4. Create response
71
- const response = NextResponse.next();
70
+ // 4. Create response with locale header for Server Components
71
+ const response = NextResponse.next({
72
+ headers: {
73
+ "x-locale": result.locale,
74
+ },
75
+ });
72
76
 
73
77
  // 5. Set cookie if needed
74
78
  if (cookie && result.shouldSetCookie) {
package/src/server.ts CHANGED
@@ -1,23 +1,146 @@
1
1
  import { getRequestConfig } from "next-intl/server";
2
+ import { createI18nCore } from "@better-i18n/core";
3
+ import type { I18nCore, Messages } from "@better-i18n/core";
2
4
 
3
- import { normalizeConfig } from "./config";
4
- import { getLocales, getMessages } from "./core";
5
- import type { I18nConfig } from "./types";
5
+ import { normalizeConfig, getProjectBaseUrl } from "./config";
6
+ import type { I18nConfig, NextFetchRequestInit } from "./types";
6
7
 
8
+ /**
9
+ * Next.js i18n core instance with ISR support
10
+ */
11
+ export interface NextI18nCore extends I18nCore {
12
+ /**
13
+ * Get messages for a locale with Next.js ISR revalidation
14
+ */
15
+ getMessages: (locale: string) => Promise<Messages>;
16
+ }
17
+
18
+ /**
19
+ * Create a fetch function with Next.js ISR revalidation
20
+ */
21
+ const createIsrFetch = (revalidateSeconds: number): typeof fetch => {
22
+ return (input: RequestInfo | URL, init?: RequestInit) => {
23
+ const nextInit: NextFetchRequestInit = {
24
+ ...init,
25
+ next: { revalidate: revalidateSeconds },
26
+ };
27
+ return fetch(input, nextInit);
28
+ };
29
+ };
30
+
31
+ /**
32
+ * Create a Next.js i18n core instance with ISR support
33
+ *
34
+ * This wraps the framework-agnostic `createI18nCore` from `@better-i18n/core`
35
+ * and adds Next.js-specific ISR revalidation for optimal caching.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const i18n = createNextI18nCore({
40
+ * project: 'acme/dashboard',
41
+ * defaultLocale: 'en',
42
+ * })
43
+ *
44
+ * // Manifest cached for 1 hour (ISR)
45
+ * const locales = await i18n.getLocales()
46
+ *
47
+ * // Messages revalidated every 30s (ISR)
48
+ * const messages = await i18n.getMessages('en')
49
+ * ```
50
+ */
51
+ export const createNextI18nCore = (config: I18nConfig): NextI18nCore => {
52
+ const normalized = normalizeConfig(config);
53
+
54
+ // Core instance uses ISR fetch for manifest (default 3600s)
55
+ const manifestFetch = createIsrFetch(normalized.manifestRevalidateSeconds);
56
+ const i18nCore = createI18nCore({
57
+ project: normalized.project,
58
+ defaultLocale: normalized.defaultLocale,
59
+ cdnBaseUrl: normalized.cdnBaseUrl,
60
+ manifestCacheTtlMs: normalized.manifestCacheTtlMs,
61
+ debug: normalized.debug,
62
+ logLevel: normalized.logLevel,
63
+ fetch: manifestFetch,
64
+ });
65
+
66
+ // Messages use separate ISR fetch with shorter revalidation (default 30s)
67
+ const messagesFetch = createIsrFetch(normalized.messagesRevalidateSeconds);
68
+
69
+ return {
70
+ ...i18nCore,
71
+ getMessages: async (locale: string): Promise<Messages> => {
72
+ const url = `${getProjectBaseUrl(normalized)}/${locale}/translations.json`;
73
+ const response = await messagesFetch(url);
74
+ if (!response.ok) {
75
+ throw new Error(`[better-i18n] Messages fetch failed for locale "${locale}" (${response.status})`);
76
+ }
77
+ return response.json();
78
+ },
79
+ };
80
+ };
81
+
82
+ /**
83
+ * Create next-intl request config for App Router
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * // i18n/request.ts
88
+ * import { createNextIntlRequestConfig } from '@better-i18n/next/server'
89
+ *
90
+ * export default createNextIntlRequestConfig({
91
+ * project: 'acme/dashboard',
92
+ * defaultLocale: 'en',
93
+ * })
94
+ * ```
95
+ */
7
96
  export const createNextIntlRequestConfig = (config: I18nConfig) =>
8
97
  getRequestConfig(async ({ requestLocale }) => {
9
- const locales = await getLocales(config);
98
+ const i18n = createNextI18nCore(config);
99
+ const normalized = normalizeConfig(config);
100
+ const locales = await i18n.getLocales();
10
101
  let locale = await requestLocale;
11
102
 
12
103
  if (!locale || !locales.includes(locale)) {
13
- locale = normalizeConfig(config).defaultLocale;
104
+ locale = normalized.defaultLocale;
14
105
  }
15
106
 
16
107
  return {
17
108
  locale,
18
- messages: await getMessages(config, locale),
109
+ messages: await i18n.getMessages(locale),
19
110
  };
20
111
  });
21
112
 
22
- export { fetchManifest, getLocales, getManifest, getManifestLanguages, getMessages } from "./core";
23
- export type { LanguageOption, ManifestLanguage, ManifestResponse, Messages } from "./types";
113
+ // Convenience exports for backwards compatibility
114
+
115
+ /**
116
+ * Fetch manifest from CDN
117
+ */
118
+ export const fetchManifest = (config: I18nConfig) =>
119
+ createNextI18nCore(config).getManifest();
120
+
121
+ /**
122
+ * Get manifest with caching
123
+ */
124
+ export const getManifest = (config: I18nConfig, options?: { forceRefresh?: boolean }) =>
125
+ createNextI18nCore(config).getManifest(options);
126
+
127
+ /**
128
+ * Get available locale codes
129
+ */
130
+ export const getLocales = (config: I18nConfig) =>
131
+ createNextI18nCore(config).getLocales();
132
+
133
+ /**
134
+ * Get messages for a locale
135
+ */
136
+ export const getMessages = (config: I18nConfig, locale: string) =>
137
+ createNextI18nCore(config).getMessages(locale);
138
+
139
+ /**
140
+ * Get language options with metadata
141
+ */
142
+ export const getManifestLanguages = (config: I18nConfig) =>
143
+ createNextI18nCore(config).getLanguages();
144
+
145
+ // Re-export types from core
146
+ export type { LanguageOption, ManifestLanguage, ManifestResponse, Messages } from "@better-i18n/core";
package/src/core.ts DELETED
@@ -1,147 +0,0 @@
1
- import { getProjectBaseUrl, normalizeConfig } from "./config";
2
- import { createLogger } from "./logger";
3
- import { extractLanguages } from "./manifest";
4
- import type {
5
- I18nConfig,
6
- LanguageOption,
7
- ManifestResponse,
8
- Messages,
9
- NextFetchRequestInit,
10
- NormalizedConfig,
11
- } from "./types";
12
-
13
- type ManifestCacheEntry = {
14
- value: ManifestResponse;
15
- expiresAt: number;
16
- };
17
-
18
- const manifestCache = new Map<string, ManifestCacheEntry>();
19
-
20
- const buildCacheKey = (config: NormalizedConfig) =>
21
- `${config.cdnBaseUrl}|${config.project}`;
22
-
23
- const withRevalidate = (
24
- init: NextFetchRequestInit,
25
- revalidateSeconds?: number,
26
- ): NextFetchRequestInit => {
27
- if (!revalidateSeconds || revalidateSeconds <= 0) {
28
- return init;
29
- }
30
-
31
- const { cache, ...rest } = init;
32
-
33
- return {
34
- ...rest,
35
- next: {
36
- ...init.next,
37
- revalidate: revalidateSeconds,
38
- },
39
- };
40
- };
41
-
42
- export const fetchManifest = async (
43
- config: I18nConfig,
44
- init: NextFetchRequestInit = {},
45
- ): Promise<ManifestResponse> => {
46
- const normalized = normalizeConfig(config);
47
- const logger = createLogger(normalized, "manifest");
48
- const url = `${getProjectBaseUrl(normalized)}/manifest.json`;
49
- const requestInit = withRevalidate(
50
- {
51
- ...init,
52
- },
53
- normalized.manifestRevalidateSeconds,
54
- );
55
-
56
- logger.debug("fetch", url);
57
-
58
- const fetchFn = normalized.fetch ?? fetch;
59
- const response = await fetchFn(url, requestInit);
60
-
61
- if (!response.ok) {
62
- const message = `[better-i18n] Manifest fetch failed (${response.status})`;
63
- logger.error(message);
64
- throw new Error(message);
65
- }
66
-
67
- const data = (await response.json()) as ManifestResponse;
68
- if (!Array.isArray(data.languages)) {
69
- throw new Error("[better-i18n] Manifest payload missing languages array");
70
- }
71
-
72
- logger.debug("fetched", { languages: data.languages.length });
73
- return data;
74
- };
75
-
76
- export const getManifest = async (
77
- config: I18nConfig,
78
- options: { forceRefresh?: boolean } = {},
79
- ): Promise<ManifestResponse> => {
80
- const normalized = normalizeConfig(config);
81
- const cacheKey = buildCacheKey(normalized);
82
- const now = Date.now();
83
- const cached = manifestCache.get(cacheKey);
84
-
85
- if (!options.forceRefresh && cached && cached.expiresAt > now) {
86
- return cached.value;
87
- }
88
-
89
- const manifest = await fetchManifest(normalized);
90
- manifestCache.set(cacheKey, {
91
- value: manifest,
92
- expiresAt: now + (normalized.manifestCacheTtlMs ?? 0),
93
- });
94
-
95
- return manifest;
96
- };
97
-
98
- export const getLocales = async (config: I18nConfig): Promise<string[]> => {
99
- const manifest = await getManifest(config);
100
- const languages = extractLanguages(manifest);
101
-
102
- if (languages.length === 0) {
103
- throw new Error("[better-i18n] No locales found in manifest");
104
- }
105
-
106
- return languages.map((language) => language.code);
107
- };
108
-
109
- export const getMessages = async (
110
- config: I18nConfig,
111
- locale: string,
112
- ): Promise<Messages> => {
113
- const normalized = normalizeConfig(config);
114
- const logger = createLogger(normalized, "messages");
115
- const url = `${getProjectBaseUrl(normalized)}/${locale}/translations.json`;
116
-
117
- const requestInit = withRevalidate(
118
- {},
119
- normalized.messagesRevalidateSeconds,
120
- );
121
-
122
- logger.debug("fetch", url);
123
-
124
- const fetchFn = normalized.fetch ?? fetch;
125
- const response = await fetchFn(url, requestInit);
126
-
127
- if (!response.ok) {
128
- const message = `[better-i18n] Messages fetch failed (${response.status})`;
129
- logger.error(message);
130
- throw new Error(message);
131
- }
132
-
133
- return (await response.json()) as Messages;
134
- };
135
-
136
- export const getManifestLanguages = async (
137
- config: I18nConfig,
138
- ): Promise<LanguageOption[]> => {
139
- const manifest = await getManifest(config);
140
- const languages = extractLanguages(manifest);
141
-
142
- if (languages.length === 0) {
143
- throw new Error("[better-i18n] No languages found in manifest");
144
- }
145
-
146
- return languages;
147
- };
package/src/logger.ts DELETED
@@ -1,68 +0,0 @@
1
- import type { Logger, LogLevel } from "@better-i18n/core";
2
- import type { I18nConfig } from "./types";
3
-
4
- const LEVEL_RANK: Record<LogLevel, number> = {
5
- debug: 0,
6
- info: 1,
7
- warn: 2,
8
- error: 3,
9
- silent: 4,
10
- };
11
-
12
- const getEnv = (key: string): string | undefined => {
13
- if (typeof process === "undefined" || !process.env) {
14
- return undefined;
15
- }
16
- return process.env[key];
17
- };
18
-
19
- const normalizeLevel = (level?: string): LogLevel | undefined => {
20
- if (!level) return undefined;
21
- const normalized = level.toLowerCase();
22
- if (normalized in LEVEL_RANK) {
23
- return normalized as LogLevel;
24
- }
25
- return undefined;
26
- };
27
-
28
- const resolveLevel = (config: I18nConfig): LogLevel => {
29
- const envDebug = getEnv("BETTER_I18N_DEBUG");
30
- if (envDebug && envDebug !== "0" && envDebug.toLowerCase() !== "false") {
31
- return "debug";
32
- }
33
-
34
- const envLevel = normalizeLevel(getEnv("BETTER_I18N_LOG_LEVEL"));
35
- if (envLevel) return envLevel;
36
-
37
- if (config.logLevel) return config.logLevel;
38
- if (config.debug) return "debug";
39
-
40
- return "warn";
41
- };
42
-
43
- const shouldLog = (current: LogLevel, target: LogLevel) =>
44
- LEVEL_RANK[target] >= LEVEL_RANK[current];
45
-
46
- export const createLogger = (config: I18nConfig, scope = "core"): Logger => {
47
- const level = resolveLevel(config);
48
- const prefix = `[better-i18n:${scope}]`;
49
-
50
- return {
51
- debug: (...args: unknown[]) => {
52
- if (!shouldLog(level, "debug")) return;
53
- console.debug(prefix, ...args);
54
- },
55
- info: (...args: unknown[]) => {
56
- if (!shouldLog(level, "info")) return;
57
- console.info(prefix, ...args);
58
- },
59
- warn: (...args: unknown[]) => {
60
- if (!shouldLog(level, "warn")) return;
61
- console.warn(prefix, ...args);
62
- },
63
- error: (...args: unknown[]) => {
64
- if (!shouldLog(level, "error")) return;
65
- console.error(prefix, ...args);
66
- },
67
- };
68
- };
package/src/manifest.ts DELETED
@@ -1,2 +0,0 @@
1
- // Re-export from i18n-core
2
- export { extractLanguages } from "@better-i18n/core";