@mpgd/i18n 0.1.0
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/LICENSE +21 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +36 -0
- package/dist/paraglide/README.md +162 -0
- package/dist/paraglide/messages/_index.d.ts +55 -0
- package/dist/paraglide/messages/_index.js +56 -0
- package/dist/paraglide/messages/action_interstitial_ads.d.ts +16 -0
- package/dist/paraglide/messages/action_interstitial_ads.js +29 -0
- package/dist/paraglide/messages/action_leaderboard.d.ts +16 -0
- package/dist/paraglide/messages/action_leaderboard.js +29 -0
- package/dist/paraglide/messages/action_localization.d.ts +16 -0
- package/dist/paraglide/messages/action_localization.js +29 -0
- package/dist/paraglide/messages/action_purchases.d.ts +16 -0
- package/dist/paraglide/messages/action_purchases.js +29 -0
- package/dist/paraglide/messages/action_reward_ads.d.ts +16 -0
- package/dist/paraglide/messages/action_reward_ads.js +29 -0
- package/dist/paraglide/messages/app_title.d.ts +16 -0
- package/dist/paraglide/messages/app_title.js +29 -0
- package/dist/paraglide/messages/availability_off.d.ts +16 -0
- package/dist/paraglide/messages/availability_off.js +29 -0
- package/dist/paraglide/messages/availability_on.d.ts +16 -0
- package/dist/paraglide/messages/availability_on.js +29 -0
- package/dist/paraglide/messages/availability_unsupported.d.ts +16 -0
- package/dist/paraglide/messages/availability_unsupported.js +29 -0
- package/dist/paraglide/messages/cap_iap.d.ts +16 -0
- package/dist/paraglide/messages/cap_iap.js +29 -0
- package/dist/paraglide/messages/cap_leaderboard.d.ts +16 -0
- package/dist/paraglide/messages/cap_leaderboard.js +29 -0
- package/dist/paraglide/messages/cap_localization.d.ts +16 -0
- package/dist/paraglide/messages/cap_localization.js +29 -0
- package/dist/paraglide/messages/cap_rewarded_ads.d.ts +16 -0
- package/dist/paraglide/messages/cap_rewarded_ads.js +29 -0
- package/dist/paraglide/messages/cap_save.d.ts +16 -0
- package/dist/paraglide/messages/cap_save.js +29 -0
- package/dist/paraglide/messages/effective_config_summary.d.ts +20 -0
- package/dist/paraglide/messages/effective_config_summary.js +29 -0
- package/dist/paraglide/messages/feature_target_disabled.d.ts +18 -0
- package/dist/paraglide/messages/feature_target_disabled.js +29 -0
- package/dist/paraglide/messages/feature_unavailable.d.ts +18 -0
- package/dist/paraglide/messages/feature_unavailable.js +29 -0
- package/dist/paraglide/messages/feature_unsupported.d.ts +18 -0
- package/dist/paraglide/messages/feature_unsupported.js +29 -0
- package/dist/paraglide/messages/leaderboard_action.d.ts +16 -0
- package/dist/paraglide/messages/leaderboard_action.js +29 -0
- package/dist/paraglide/messages/leaderboard_action_unavailable.d.ts +16 -0
- package/dist/paraglide/messages/leaderboard_action_unavailable.js +29 -0
- package/dist/paraglide/messages/leaderboard_opened.d.ts +16 -0
- package/dist/paraglide/messages/leaderboard_opened.js +29 -0
- package/dist/paraglide/messages/leaderboard_unavailable.d.ts +16 -0
- package/dist/paraglide/messages/leaderboard_unavailable.js +29 -0
- package/dist/paraglide/messages/loading_player.d.ts +16 -0
- package/dist/paraglide/messages/loading_player.js +29 -0
- package/dist/paraglide/messages/mock_only.d.ts +16 -0
- package/dist/paraglide/messages/mock_only.js +29 -0
- package/dist/paraglide/messages/opening_purchase.d.ts +16 -0
- package/dist/paraglide/messages/opening_purchase.js +29 -0
- package/dist/paraglide/messages/package.json +4 -0
- package/dist/paraglide/messages/play_again.d.ts +16 -0
- package/dist/paraglide/messages/play_again.js +29 -0
- package/dist/paraglide/messages/player.d.ts +18 -0
- package/dist/paraglide/messages/player.js +29 -0
- package/dist/paraglide/messages/preparing_demo.d.ts +16 -0
- package/dist/paraglide/messages/preparing_demo.js +29 -0
- package/dist/paraglide/messages/purchase_action.d.ts +16 -0
- package/dist/paraglide/messages/purchase_action.js +29 -0
- package/dist/paraglide/messages/purchase_completed.d.ts +16 -0
- package/dist/paraglide/messages/purchase_completed.js +29 -0
- package/dist/paraglide/messages/purchase_status.d.ts +18 -0
- package/dist/paraglide/messages/purchase_status.js +29 -0
- package/dist/paraglide/messages/purchase_unavailable.d.ts +16 -0
- package/dist/paraglide/messages/purchase_unavailable.js +29 -0
- package/dist/paraglide/messages/reward_ad_action.d.ts +16 -0
- package/dist/paraglide/messages/reward_ad_action.js +29 -0
- package/dist/paraglide/messages/reward_ad_unavailable.d.ts +16 -0
- package/dist/paraglide/messages/reward_ad_unavailable.js +29 -0
- package/dist/paraglide/messages/reward_granted.d.ts +16 -0
- package/dist/paraglide/messages/reward_granted.js +29 -0
- package/dist/paraglide/messages/reward_unavailable.d.ts +18 -0
- package/dist/paraglide/messages/reward_unavailable.js +29 -0
- package/dist/paraglide/messages/save_summary.d.ts +19 -0
- package/dist/paraglide/messages/save_summary.js +29 -0
- package/dist/paraglide/messages/saved.d.ts +16 -0
- package/dist/paraglide/messages/saved.js +29 -0
- package/dist/paraglide/messages/saved_and_submitted.d.ts +16 -0
- package/dist/paraglide/messages/saved_and_submitted.js +29 -0
- package/dist/paraglide/messages/saving_result.d.ts +16 -0
- package/dist/paraglide/messages/saving_result.js +29 -0
- package/dist/paraglide/messages/score.d.ts +18 -0
- package/dist/paraglide/messages/score.js +29 -0
- package/dist/paraglide/messages/sdk_summary.d.ts +18 -0
- package/dist/paraglide/messages/sdk_summary.js +29 -0
- package/dist/paraglide/messages/showing_rewarded_ad.d.ts +16 -0
- package/dist/paraglide/messages/showing_rewarded_ad.js +29 -0
- package/dist/paraglide/messages/status_cleared.d.ts +16 -0
- package/dist/paraglide/messages/status_cleared.js +29 -0
- package/dist/paraglide/messages/status_try_again.d.ts +16 -0
- package/dist/paraglide/messages/status_try_again.js +29 -0
- package/dist/paraglide/messages/tap_to_start.d.ts +16 -0
- package/dist/paraglide/messages/tap_to_start.js +29 -0
- package/dist/paraglide/messages/target.d.ts +18 -0
- package/dist/paraglide/messages/target.js +29 -0
- package/dist/paraglide/messages/target_availability_summary.d.ts +19 -0
- package/dist/paraglide/messages/target_availability_summary.js +29 -0
- package/dist/paraglide/messages/target_config_unavailable.d.ts +16 -0
- package/dist/paraglide/messages/target_config_unavailable.js +29 -0
- package/dist/paraglide/messages/target_feature_iap.d.ts +16 -0
- package/dist/paraglide/messages/target_feature_iap.js +29 -0
- package/dist/paraglide/messages/target_feature_interstitial_ads.d.ts +16 -0
- package/dist/paraglide/messages/target_feature_interstitial_ads.js +29 -0
- package/dist/paraglide/messages/target_feature_leaderboard.d.ts +16 -0
- package/dist/paraglide/messages/target_feature_leaderboard.js +29 -0
- package/dist/paraglide/messages/target_feature_localization.d.ts +16 -0
- package/dist/paraglide/messages/target_feature_localization.js +29 -0
- package/dist/paraglide/messages/target_feature_rewarded_ads.d.ts +16 -0
- package/dist/paraglide/messages/target_feature_rewarded_ads.js +29 -0
- package/dist/paraglide/messages.d.ts +2 -0
- package/dist/paraglide/messages.js +4 -0
- package/dist/paraglide/registry.d.ts +34 -0
- package/dist/paraglide/registry.js +46 -0
- package/dist/paraglide/runtime.d.ts +758 -0
- package/dist/paraglide/runtime.js +1852 -0
- package/dist/paraglide/server.d.ts +96 -0
- package/dist/paraglide/server.js +282 -0
- package/dist/paraglideAdapter.d.ts +86 -0
- package/dist/paraglideAdapter.js +2 -0
- package/messages/en.json +57 -0
- package/messages/ko.json +57 -0
- package/package.json +69 -0
- package/project.inlang/settings.json +10 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server middleware that handles locale-based routing and request processing.
|
|
3
|
+
*
|
|
4
|
+
* Configure `disableAsyncLocalStorage` when generating Paraglide with
|
|
5
|
+
* `paraglideVitePlugin()` or `compile()`, not when calling
|
|
6
|
+
* `paraglideMiddleware()`. Keep AsyncLocalStorage enabled by default and
|
|
7
|
+
* only disable it for runtimes that lack `AsyncLocalStorage` support and
|
|
8
|
+
* guarantee request isolation.
|
|
9
|
+
*
|
|
10
|
+
* This middleware performs several key functions:
|
|
11
|
+
*
|
|
12
|
+
* 1. Determines the locale for the incoming request using configured strategies
|
|
13
|
+
* 2. Handles URL localization and redirects (only for document requests)
|
|
14
|
+
* 3. Maintains locale state using AsyncLocalStorage to prevent request interference
|
|
15
|
+
*
|
|
16
|
+
* When URL strategy is used:
|
|
17
|
+
*
|
|
18
|
+
* - The locale is extracted from the URL for all request types
|
|
19
|
+
* - If URL doesn't match the determined locale, redirects to localized URL (only for document requests)
|
|
20
|
+
* - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)
|
|
21
|
+
*
|
|
22
|
+
* @see https://paraglidejs.com/middleware
|
|
23
|
+
*
|
|
24
|
+
* @template T - The return type of the resolve function
|
|
25
|
+
*
|
|
26
|
+
* @param {Request} request - The incoming request object
|
|
27
|
+
* @param {(args: { request: Request, locale: import("./runtime.js").Locale }) => T | Promise<T>} resolve - Function to handle the request. The callback receives:
|
|
28
|
+
* - `request`: A modified request with a delocalized URL when the URL strategy is used (e.g., `/fr/about` → `/about`).
|
|
29
|
+
* If your framework handles URL localization itself (e.g., TanStack Router's `rewrite` option), use the original
|
|
30
|
+
* request instead to avoid redirect loops.
|
|
31
|
+
* - `locale`: The determined locale for this request.
|
|
32
|
+
* @param {{
|
|
33
|
+
* effectiveRequestUrl?: string | URL | ((request: Request) => string | URL),
|
|
34
|
+
* onRedirect?: (response: Response) => void
|
|
35
|
+
* }} [options] - Options to control middleware behavior. `effectiveRequestUrl` sets the effective request URL used for route matching, URL-based locale detection, redirects, and `getUrlOrigin()`.
|
|
36
|
+
* @returns {Promise<Response>}
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.
|
|
41
|
+
* export const handle = async ({ event, resolve }) => {
|
|
42
|
+
* return paraglideMiddleware(event.request, ({ request, locale }) => {
|
|
43
|
+
* // let the framework further resolve the request
|
|
44
|
+
* return resolve(request);
|
|
45
|
+
* });
|
|
46
|
+
* };
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* // Usage in a framework like Express JS or Hono
|
|
52
|
+
* app.use(async (req, res, next) => {
|
|
53
|
+
* const result = await paraglideMiddleware(req, ({ request, locale }) => {
|
|
54
|
+
* // If a redirect happens this won't be called
|
|
55
|
+
* return next(request);
|
|
56
|
+
* });
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* // Usage with frameworks that handle URL localization/delocalization themselves
|
|
63
|
+
* //
|
|
64
|
+
* // Some frameworks like TanStack Router handle URL localization and delocalization
|
|
65
|
+
* // themselves via their own rewrite APIs (e.g., `rewrite.input`/`rewrite.output`).
|
|
66
|
+
* //
|
|
67
|
+
* // When the framework handles this, the middleware's URL delocalization is not needed.
|
|
68
|
+
* // Using the modified `request` from the callback would cause a redirect loop because
|
|
69
|
+
* // both the middleware and the framework would attempt to delocalize the URL.
|
|
70
|
+
* //
|
|
71
|
+
* // Solution: Pass the original request to the handler instead of the modified one.
|
|
72
|
+
* // The middleware still handles locale detection, cookies, and AsyncLocalStorage context.
|
|
73
|
+
* //
|
|
74
|
+
* // ❌ WRONG - causes redirect loop when framework handles URL rewriting:
|
|
75
|
+
* // paraglideMiddleware(req, ({ request }) => handler.fetch(request))
|
|
76
|
+
* //
|
|
77
|
+
* // ✅ CORRECT - use original request when framework handles URL localization:
|
|
78
|
+
* // paraglideMiddleware(req, () => handler.fetch(req))
|
|
79
|
+
*
|
|
80
|
+
* * *
|
|
81
|
+
* export default {
|
|
82
|
+
* fetch(req: Request): Promise<Response> {
|
|
83
|
+
* // TanStack Router handles URL rewriting via deLocalizeUrl/localizeUrl
|
|
84
|
+
* // so we pass the original `req` instead of the modified `request`
|
|
85
|
+
* return paraglideMiddleware(req, () => handler.fetch(req))
|
|
86
|
+
* },
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function paraglideMiddleware<T>(request: Request, resolve: (args: {
|
|
91
|
+
request: Request;
|
|
92
|
+
locale: import("./runtime.js").Locale;
|
|
93
|
+
}) => T | Promise<T>, options?: {
|
|
94
|
+
effectiveRequestUrl?: string | URL | ((request: Request) => string | URL);
|
|
95
|
+
onRedirect?: (response: Response) => void;
|
|
96
|
+
}): Promise<Response>;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
import * as runtime from "./runtime.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Server middleware that handles locale-based routing and request processing.
|
|
7
|
+
*
|
|
8
|
+
* Configure `disableAsyncLocalStorage` when generating Paraglide with
|
|
9
|
+
* `paraglideVitePlugin()` or `compile()`, not when calling
|
|
10
|
+
* `paraglideMiddleware()`. Keep AsyncLocalStorage enabled by default and
|
|
11
|
+
* only disable it for runtimes that lack `AsyncLocalStorage` support and
|
|
12
|
+
* guarantee request isolation.
|
|
13
|
+
*
|
|
14
|
+
* This middleware performs several key functions:
|
|
15
|
+
*
|
|
16
|
+
* 1. Determines the locale for the incoming request using configured strategies
|
|
17
|
+
* 2. Handles URL localization and redirects (only for document requests)
|
|
18
|
+
* 3. Maintains locale state using AsyncLocalStorage to prevent request interference
|
|
19
|
+
*
|
|
20
|
+
* When URL strategy is used:
|
|
21
|
+
*
|
|
22
|
+
* - The locale is extracted from the URL for all request types
|
|
23
|
+
* - If URL doesn't match the determined locale, redirects to localized URL (only for document requests)
|
|
24
|
+
* - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)
|
|
25
|
+
*
|
|
26
|
+
* @see https://paraglidejs.com/middleware
|
|
27
|
+
*
|
|
28
|
+
* @template T - The return type of the resolve function
|
|
29
|
+
*
|
|
30
|
+
* @param {Request} request - The incoming request object
|
|
31
|
+
* @param {(args: { request: Request, locale: import("./runtime.js").Locale }) => T | Promise<T>} resolve - Function to handle the request. The callback receives:
|
|
32
|
+
* - `request`: A modified request with a delocalized URL when the URL strategy is used (e.g., `/fr/about` → `/about`).
|
|
33
|
+
* If your framework handles URL localization itself (e.g., TanStack Router's `rewrite` option), use the original
|
|
34
|
+
* request instead to avoid redirect loops.
|
|
35
|
+
* - `locale`: The determined locale for this request.
|
|
36
|
+
* @param {{
|
|
37
|
+
* effectiveRequestUrl?: string | URL | ((request: Request) => string | URL),
|
|
38
|
+
* onRedirect?: (response: Response) => void
|
|
39
|
+
* }} [options] - Options to control middleware behavior. `effectiveRequestUrl` sets the effective request URL used for route matching, URL-based locale detection, redirects, and `getUrlOrigin()`.
|
|
40
|
+
* @returns {Promise<Response>}
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.
|
|
45
|
+
* export const handle = async ({ event, resolve }) => {
|
|
46
|
+
* return paraglideMiddleware(event.request, ({ request, locale }) => {
|
|
47
|
+
* // let the framework further resolve the request
|
|
48
|
+
* return resolve(request);
|
|
49
|
+
* });
|
|
50
|
+
* };
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // Usage in a framework like Express JS or Hono
|
|
56
|
+
* app.use(async (req, res, next) => {
|
|
57
|
+
* const result = await paraglideMiddleware(req, ({ request, locale }) => {
|
|
58
|
+
* // If a redirect happens this won't be called
|
|
59
|
+
* return next(request);
|
|
60
|
+
* });
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* // Usage with frameworks that handle URL localization/delocalization themselves
|
|
67
|
+
* //
|
|
68
|
+
* // Some frameworks like TanStack Router handle URL localization and delocalization
|
|
69
|
+
* // themselves via their own rewrite APIs (e.g., `rewrite.input`/`rewrite.output`).
|
|
70
|
+
* //
|
|
71
|
+
* // When the framework handles this, the middleware's URL delocalization is not needed.
|
|
72
|
+
* // Using the modified `request` from the callback would cause a redirect loop because
|
|
73
|
+
* // both the middleware and the framework would attempt to delocalize the URL.
|
|
74
|
+
* //
|
|
75
|
+
* // Solution: Pass the original request to the handler instead of the modified one.
|
|
76
|
+
* // The middleware still handles locale detection, cookies, and AsyncLocalStorage context.
|
|
77
|
+
* //
|
|
78
|
+
* // ❌ WRONG - causes redirect loop when framework handles URL rewriting:
|
|
79
|
+
* // paraglideMiddleware(req, ({ request }) => handler.fetch(request))
|
|
80
|
+
* //
|
|
81
|
+
* // ✅ CORRECT - use original request when framework handles URL localization:
|
|
82
|
+
* // paraglideMiddleware(req, () => handler.fetch(req))
|
|
83
|
+
*
|
|
84
|
+
* * *
|
|
85
|
+
* export default {
|
|
86
|
+
* fetch(req: Request): Promise<Response> {
|
|
87
|
+
* // TanStack Router handles URL rewriting via deLocalizeUrl/localizeUrl
|
|
88
|
+
* // so we pass the original `req` instead of the modified `request`
|
|
89
|
+
* return paraglideMiddleware(req, () => handler.fetch(req))
|
|
90
|
+
* },
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export async function paraglideMiddleware(request, resolve, options) {
|
|
95
|
+
if (!runtime.disableAsyncLocalStorage && !runtime.serverAsyncLocalStorage) {
|
|
96
|
+
const { AsyncLocalStorage } = await import("async_hooks");
|
|
97
|
+
runtime.overwriteServerAsyncLocalStorage(new AsyncLocalStorage());
|
|
98
|
+
} else if (!runtime.serverAsyncLocalStorage) {
|
|
99
|
+
runtime.overwriteServerAsyncLocalStorage(createMockAsyncLocalStorage());
|
|
100
|
+
}
|
|
101
|
+
const url = resolveMiddlewareUrl(request, options?.effectiveRequestUrl);
|
|
102
|
+
const origin = url.origin;
|
|
103
|
+
if (runtime.isExcludedByRouteStrategy(url.href)) {
|
|
104
|
+
const locale = runtime.baseLocale;
|
|
105
|
+
const newRequest = cloneRequestWithFallback(request, url);
|
|
106
|
+
/** @type {Set<string>} */
|
|
107
|
+
const messageCalls = new Set();
|
|
108
|
+
return /** @type {Response} */ (await runtime.serverAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest })));
|
|
109
|
+
}
|
|
110
|
+
const strategy = runtime.getStrategyForUrl(url.href);
|
|
111
|
+
const decision = await runtime.shouldRedirect({ request, effectiveRequestUrl: url });
|
|
112
|
+
const locale = decision.locale;
|
|
113
|
+
// if the client makes a request to a URL that doesn't match
|
|
114
|
+
// the localizedUrl, redirect the client to the localized URL
|
|
115
|
+
if (request.headers.get("Sec-Fetch-Dest") === "document" &&
|
|
116
|
+
decision.shouldRedirect &&
|
|
117
|
+
decision.redirectUrl) {
|
|
118
|
+
// Create headers object with Vary header if preferredLanguage strategy is used
|
|
119
|
+
/** @type {Record<string, string>} */
|
|
120
|
+
const headers = {};
|
|
121
|
+
if (strategy.includes("preferredLanguage")) {
|
|
122
|
+
headers["Vary"] = "Accept-Language";
|
|
123
|
+
}
|
|
124
|
+
const response = new Response(null, {
|
|
125
|
+
status: 307,
|
|
126
|
+
headers: {
|
|
127
|
+
Location: decision.redirectUrl.href,
|
|
128
|
+
...headers,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
options?.onRedirect?.(response);
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
// If the strategy includes "url", we need to de-localize the URL
|
|
135
|
+
// before passing it to the server middleware.
|
|
136
|
+
//
|
|
137
|
+
// The middleware is responsible for mapping a localized URL to the
|
|
138
|
+
// de-localized URL e.g. `/en/about` to `/about`. Otherwise,
|
|
139
|
+
// the server can't render the correct page.
|
|
140
|
+
let newRequest;
|
|
141
|
+
if (strategy.includes("url")) {
|
|
142
|
+
newRequest = cloneRequestWithFallback(request, runtime.deLocalizeUrl(url));
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
newRequest = cloneRequestWithFallback(request, url);
|
|
146
|
+
}
|
|
147
|
+
// the message functions that have been called in this request
|
|
148
|
+
/** @type {Set<string>} */
|
|
149
|
+
const messageCalls = new Set();
|
|
150
|
+
const response = await runtime.serverAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest }));
|
|
151
|
+
// Only modify HTML responses
|
|
152
|
+
if (runtime.experimentalMiddlewareLocaleSplitting &&
|
|
153
|
+
response.headers.get("Content-Type")?.includes("html")) {
|
|
154
|
+
const body = await response.text();
|
|
155
|
+
const messages = [];
|
|
156
|
+
// using .values() to avoid polyfilling in older projects. else the following error is thrown
|
|
157
|
+
// Type 'Set<string>' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.
|
|
158
|
+
for (const messageCall of Array.from(messageCalls)) {
|
|
159
|
+
const [id, locale] =
|
|
160
|
+
/** @type {[string, import("./runtime.js").Locale]} */ (messageCall.split(":"));
|
|
161
|
+
messages.push(`${id}: ${compiledBundles[id]?.[locale]}`);
|
|
162
|
+
}
|
|
163
|
+
// Prevent translated content from terminating the inline script tag.
|
|
164
|
+
const escapedMessages = messages
|
|
165
|
+
.join(",")
|
|
166
|
+
.replace(/<\/(script)/gi, "<\\/$1");
|
|
167
|
+
// Reuse the request's CSP nonce (if any) so the injected script is allowed under a strict CSP
|
|
168
|
+
const nonce = response.headers
|
|
169
|
+
.get("Content-Security-Policy")
|
|
170
|
+
?.match(/'nonce-([\w+/=-]+)'/)?.[1];
|
|
171
|
+
const nonceAttr = nonce ? `nonce="${nonce}"` : "";
|
|
172
|
+
const script = `<script ${nonceAttr}>globalThis.__paraglide = globalThis.__paraglide ?? {}; globalThis.__paraglide.ssr = { ${escapedMessages} }</script>`;
|
|
173
|
+
// Insert the script before the closing head tag
|
|
174
|
+
const newBody = body.replace("</head>", `${script}</head>`);
|
|
175
|
+
// Create a new response with the modified body
|
|
176
|
+
// Clone all headers except Content-Length which will be set automatically
|
|
177
|
+
const newHeaders = new Headers(response.headers);
|
|
178
|
+
newHeaders.delete("Content-Length"); // Let the browser calculate the correct length
|
|
179
|
+
return new Response(newBody, {
|
|
180
|
+
status: response.status,
|
|
181
|
+
statusText: response.statusText,
|
|
182
|
+
headers: newHeaders,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* @param {Request} request
|
|
189
|
+
* @param {string | URL | ((request: Request) => string | URL) | undefined} effectiveRequestUrl
|
|
190
|
+
* @returns {URL}
|
|
191
|
+
*/
|
|
192
|
+
function resolveMiddlewareUrl(request, effectiveRequestUrl) {
|
|
193
|
+
if (typeof effectiveRequestUrl === "function") {
|
|
194
|
+
return new URL(effectiveRequestUrl(request), request.url);
|
|
195
|
+
}
|
|
196
|
+
if (typeof effectiveRequestUrl === "string" || effectiveRequestUrl instanceof URL) {
|
|
197
|
+
return new URL(effectiveRequestUrl, request.url);
|
|
198
|
+
}
|
|
199
|
+
return new URL(request.url);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Some metaframeworks (NextJS) require a new Request object.
|
|
203
|
+
* https://github.com/opral/inlang-paraglide-js/issues/411
|
|
204
|
+
*
|
|
205
|
+
* However, some frameworks (TanStack Start 1.143+) use custom Request
|
|
206
|
+
* implementations that cannot be cloned with `new Request(request)`.
|
|
207
|
+
* https://github.com/opral/paraglide-js/issues/573
|
|
208
|
+
*
|
|
209
|
+
* Effective request URL overrides behind proxies:
|
|
210
|
+
* https://github.com/opral/paraglide-js/issues/652
|
|
211
|
+
*
|
|
212
|
+
* @param {Request} request
|
|
213
|
+
* @param {string | URL} [url]
|
|
214
|
+
* @returns {Request}
|
|
215
|
+
*/
|
|
216
|
+
function cloneRequestWithFallback(request, url = request.url) {
|
|
217
|
+
const targetUrl = typeof url === "string" ? url : url.href;
|
|
218
|
+
if (targetUrl === request.url) {
|
|
219
|
+
try {
|
|
220
|
+
// Clone first so building a new Request does not consume the original body stream.
|
|
221
|
+
return new Request(request.clone());
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
try {
|
|
225
|
+
return new Request(request);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return request;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
// Clone first so building a new Request does not consume the original body stream.
|
|
234
|
+
return new Request(targetUrl, request.clone());
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
try {
|
|
238
|
+
return new Request(targetUrl, request);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return request;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Creates a mock AsyncLocalStorage implementation for environments where
|
|
247
|
+
* native AsyncLocalStorage is not available or disabled.
|
|
248
|
+
*
|
|
249
|
+
* This mock implementation mimics the behavior of the native AsyncLocalStorage
|
|
250
|
+
* but doesn't require the async_hooks module. It's used as a fallback when
|
|
251
|
+
* the runtime does not expose AsyncLocalStorage or when it has been disabled.
|
|
252
|
+
*
|
|
253
|
+
* @returns {import("./runtime.js").ParaglideAsyncLocalStorage}
|
|
254
|
+
*/
|
|
255
|
+
function createMockAsyncLocalStorage() {
|
|
256
|
+
/** @type {any} */
|
|
257
|
+
let currentStore = undefined;
|
|
258
|
+
return {
|
|
259
|
+
getStore() {
|
|
260
|
+
return currentStore;
|
|
261
|
+
},
|
|
262
|
+
async run(store, callback) {
|
|
263
|
+
currentStore = store;
|
|
264
|
+
try {
|
|
265
|
+
return await callback();
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
currentStore = undefined;
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// Used in generated server.js when async local storage is disabled.
|
|
274
|
+
void createMockAsyncLocalStorage;
|
|
275
|
+
/**
|
|
276
|
+
* The compiled messages for the server middleware.
|
|
277
|
+
*
|
|
278
|
+
* Only populated if `enableMiddlewareOptimizations` is set to `true`.
|
|
279
|
+
*
|
|
280
|
+
* @type {Record<string, Record<import("./runtime.js").Locale, string>>}
|
|
281
|
+
*/
|
|
282
|
+
const compiledBundles = {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type Locale = 'en' | 'ko';
|
|
2
|
+
|
|
3
|
+
export interface MessageOptions {
|
|
4
|
+
readonly locale?: Locale;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type EmptyMessage = (
|
|
8
|
+
inputs?: Record<string, never>,
|
|
9
|
+
options?: MessageOptions,
|
|
10
|
+
) => string;
|
|
11
|
+
|
|
12
|
+
export type MessageFunction<TInputs extends Record<string, string | number>> = (
|
|
13
|
+
inputs: TInputs,
|
|
14
|
+
options?: MessageOptions,
|
|
15
|
+
) => string;
|
|
16
|
+
|
|
17
|
+
export interface MpgdMessages {
|
|
18
|
+
readonly action_interstitial_ads: EmptyMessage;
|
|
19
|
+
readonly action_leaderboard: EmptyMessage;
|
|
20
|
+
readonly action_localization: EmptyMessage;
|
|
21
|
+
readonly action_purchases: EmptyMessage;
|
|
22
|
+
readonly action_reward_ads: EmptyMessage;
|
|
23
|
+
readonly app_title: EmptyMessage;
|
|
24
|
+
readonly availability_off: EmptyMessage;
|
|
25
|
+
readonly availability_on: EmptyMessage;
|
|
26
|
+
readonly availability_unsupported: EmptyMessage;
|
|
27
|
+
readonly cap_iap: EmptyMessage;
|
|
28
|
+
readonly cap_leaderboard: EmptyMessage;
|
|
29
|
+
readonly cap_localization: EmptyMessage;
|
|
30
|
+
readonly cap_rewarded_ads: EmptyMessage;
|
|
31
|
+
readonly cap_save: EmptyMessage;
|
|
32
|
+
readonly effective_config_summary: MessageFunction<{
|
|
33
|
+
readonly products: number;
|
|
34
|
+
readonly ads: number;
|
|
35
|
+
readonly storage: string;
|
|
36
|
+
}>;
|
|
37
|
+
readonly feature_target_disabled: MessageFunction<{ readonly feature: string }>;
|
|
38
|
+
readonly feature_unavailable: MessageFunction<{ readonly feature: string }>;
|
|
39
|
+
readonly feature_unsupported: MessageFunction<{ readonly feature: string }>;
|
|
40
|
+
readonly leaderboard_action: EmptyMessage;
|
|
41
|
+
readonly leaderboard_action_unavailable: EmptyMessage;
|
|
42
|
+
readonly leaderboard_opened: EmptyMessage;
|
|
43
|
+
readonly leaderboard_unavailable: EmptyMessage;
|
|
44
|
+
readonly loading_player: EmptyMessage;
|
|
45
|
+
readonly mock_only: EmptyMessage;
|
|
46
|
+
readonly opening_purchase: EmptyMessage;
|
|
47
|
+
readonly player: MessageFunction<{ readonly name: string }>;
|
|
48
|
+
readonly play_again: EmptyMessage;
|
|
49
|
+
readonly preparing_demo: EmptyMessage;
|
|
50
|
+
readonly purchase_action: EmptyMessage;
|
|
51
|
+
readonly purchase_completed: EmptyMessage;
|
|
52
|
+
readonly purchase_status: MessageFunction<{ readonly status: string }>;
|
|
53
|
+
readonly purchase_unavailable: EmptyMessage;
|
|
54
|
+
readonly reward_ad_action: EmptyMessage;
|
|
55
|
+
readonly reward_ad_unavailable: EmptyMessage;
|
|
56
|
+
readonly reward_granted: EmptyMessage;
|
|
57
|
+
readonly reward_unavailable: MessageFunction<{ readonly status: string }>;
|
|
58
|
+
readonly save_summary: MessageFunction<{
|
|
59
|
+
readonly bestScore: number;
|
|
60
|
+
readonly coins: number;
|
|
61
|
+
}>;
|
|
62
|
+
readonly saved: EmptyMessage;
|
|
63
|
+
readonly saved_and_submitted: EmptyMessage;
|
|
64
|
+
readonly saving_result: EmptyMessage;
|
|
65
|
+
readonly score: MessageFunction<{ readonly score: number }>;
|
|
66
|
+
readonly sdk_summary: MessageFunction<{ readonly features: string }>;
|
|
67
|
+
readonly showing_rewarded_ad: EmptyMessage;
|
|
68
|
+
readonly status_cleared: EmptyMessage;
|
|
69
|
+
readonly status_try_again: EmptyMessage;
|
|
70
|
+
readonly tap_to_start: EmptyMessage;
|
|
71
|
+
readonly target: MessageFunction<{ readonly target: string }>;
|
|
72
|
+
readonly target_availability_summary: MessageFunction<{
|
|
73
|
+
readonly target: string;
|
|
74
|
+
readonly summary: string;
|
|
75
|
+
}>;
|
|
76
|
+
readonly target_config_unavailable: EmptyMessage;
|
|
77
|
+
readonly target_feature_iap: EmptyMessage;
|
|
78
|
+
readonly target_feature_interstitial_ads: EmptyMessage;
|
|
79
|
+
readonly target_feature_leaderboard: EmptyMessage;
|
|
80
|
+
readonly target_feature_localization: EmptyMessage;
|
|
81
|
+
readonly target_feature_rewarded_ads: EmptyMessage;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const m: MpgdMessages;
|
|
85
|
+
export const baseLocale: Locale;
|
|
86
|
+
export const locales: readonly Locale[];
|
package/messages/en.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
3
|
+
"action_localization": "Localization",
|
|
4
|
+
"action_interstitial_ads": "Interstitial ads",
|
|
5
|
+
"action_leaderboard": "Leaderboard",
|
|
6
|
+
"action_purchases": "Purchases",
|
|
7
|
+
"action_reward_ads": "Reward ads",
|
|
8
|
+
"app_title": "MPGD Kit",
|
|
9
|
+
"availability_off": "off",
|
|
10
|
+
"availability_on": "on",
|
|
11
|
+
"availability_unsupported": "n/a",
|
|
12
|
+
"cap_localization": "localization",
|
|
13
|
+
"cap_iap": "iap",
|
|
14
|
+
"cap_leaderboard": "leaderboard",
|
|
15
|
+
"cap_rewarded_ads": "rewarded ads",
|
|
16
|
+
"cap_save": "save",
|
|
17
|
+
"effective_config_summary": "Products {products}, ads {ads}, storage {storage}",
|
|
18
|
+
"feature_target_disabled": "{feature} disabled for this target.",
|
|
19
|
+
"feature_unavailable": "{feature} unavailable.",
|
|
20
|
+
"feature_unsupported": "{feature} unsupported on this target.",
|
|
21
|
+
"leaderboard_action": "Open leaderboard",
|
|
22
|
+
"leaderboard_action_unavailable": "Leaderboard unavailable",
|
|
23
|
+
"leaderboard_opened": "Leaderboard opened.",
|
|
24
|
+
"leaderboard_unavailable": "Leaderboard unavailable.",
|
|
25
|
+
"loading_player": "Loading player...",
|
|
26
|
+
"mock_only": "mock only",
|
|
27
|
+
"opening_purchase": "Opening mock purchase...",
|
|
28
|
+
"player": "Player: {name}",
|
|
29
|
+
"play_again": "Play again",
|
|
30
|
+
"preparing_demo": "Preparing SDK demo...",
|
|
31
|
+
"purchase_action": "Buy 100 coins",
|
|
32
|
+
"purchase_completed": "Purchase completed.",
|
|
33
|
+
"purchase_status": "Purchase {status}.",
|
|
34
|
+
"purchase_unavailable": "Purchase unavailable",
|
|
35
|
+
"reward_ad_action": "Reward ad +10 coins",
|
|
36
|
+
"reward_ad_unavailable": "Reward ad unavailable",
|
|
37
|
+
"reward_granted": "Reward granted.",
|
|
38
|
+
"reward_unavailable": "Reward unavailable: {status}",
|
|
39
|
+
"save_summary": "Best {bestScore} Coins {coins}",
|
|
40
|
+
"saved": "Saved.",
|
|
41
|
+
"saved_and_submitted": "Saved and submitted.",
|
|
42
|
+
"saving_result": "Saving result...",
|
|
43
|
+
"score": "Score {score}",
|
|
44
|
+
"sdk_summary": "SDK: {features}",
|
|
45
|
+
"showing_rewarded_ad": "Showing rewarded ad...",
|
|
46
|
+
"status_cleared": "Cleared",
|
|
47
|
+
"status_try_again": "Try Again",
|
|
48
|
+
"tap_to_start": "Tap or press Enter",
|
|
49
|
+
"target": "Target: {target}",
|
|
50
|
+
"target_availability_summary": "Target {target}: {summary}",
|
|
51
|
+
"target_config_unavailable": "Target config: unavailable",
|
|
52
|
+
"target_feature_iap": "IAP",
|
|
53
|
+
"target_feature_interstitial_ads": "Inter",
|
|
54
|
+
"target_feature_leaderboard": "Board",
|
|
55
|
+
"target_feature_localization": "Localization",
|
|
56
|
+
"target_feature_rewarded_ads": "Reward"
|
|
57
|
+
}
|
package/messages/ko.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
3
|
+
"action_localization": "다국어",
|
|
4
|
+
"action_interstitial_ads": "전면광고",
|
|
5
|
+
"action_leaderboard": "랭킹",
|
|
6
|
+
"action_purchases": "구매",
|
|
7
|
+
"action_reward_ads": "보상광고",
|
|
8
|
+
"app_title": "MPGD 키트",
|
|
9
|
+
"availability_off": "꺼짐",
|
|
10
|
+
"availability_on": "켜짐",
|
|
11
|
+
"availability_unsupported": "미지원",
|
|
12
|
+
"cap_localization": "다국어",
|
|
13
|
+
"cap_iap": "결제",
|
|
14
|
+
"cap_leaderboard": "랭킹",
|
|
15
|
+
"cap_rewarded_ads": "보상광고",
|
|
16
|
+
"cap_save": "저장",
|
|
17
|
+
"effective_config_summary": "상품 {products}, 광고 {ads}, 저장소 {storage}",
|
|
18
|
+
"feature_target_disabled": "{feature} 타깃 비활성.",
|
|
19
|
+
"feature_unavailable": "{feature} 사용 불가.",
|
|
20
|
+
"feature_unsupported": "{feature} 이 타깃에서 미지원.",
|
|
21
|
+
"leaderboard_action": "랭킹 열기",
|
|
22
|
+
"leaderboard_action_unavailable": "랭킹 사용 불가",
|
|
23
|
+
"leaderboard_opened": "랭킹 열림.",
|
|
24
|
+
"leaderboard_unavailable": "랭킹 사용 불가.",
|
|
25
|
+
"loading_player": "플레이어 불러오는 중...",
|
|
26
|
+
"mock_only": "목업 전용",
|
|
27
|
+
"opening_purchase": "목업 구매 여는 중...",
|
|
28
|
+
"player": "플레이어: {name}",
|
|
29
|
+
"play_again": "다시 플레이",
|
|
30
|
+
"preparing_demo": "SDK 데모 준비 중...",
|
|
31
|
+
"purchase_action": "100 코인 구매",
|
|
32
|
+
"purchase_completed": "구매 완료.",
|
|
33
|
+
"purchase_status": "구매 {status}.",
|
|
34
|
+
"purchase_unavailable": "구매 사용 불가",
|
|
35
|
+
"reward_ad_action": "보상광고 +10 코인",
|
|
36
|
+
"reward_ad_unavailable": "보상광고 사용 불가",
|
|
37
|
+
"reward_granted": "보상 지급 완료.",
|
|
38
|
+
"reward_unavailable": "보상 사용 불가: {status}",
|
|
39
|
+
"save_summary": "최고 {bestScore} 코인 {coins}",
|
|
40
|
+
"saved": "저장됨.",
|
|
41
|
+
"saved_and_submitted": "저장 및 랭킹 제출 완료.",
|
|
42
|
+
"saving_result": "결과 저장 중...",
|
|
43
|
+
"score": "점수 {score}",
|
|
44
|
+
"sdk_summary": "SDK: {features}",
|
|
45
|
+
"showing_rewarded_ad": "보상광고 표시 중...",
|
|
46
|
+
"status_cleared": "클리어",
|
|
47
|
+
"status_try_again": "다시 도전",
|
|
48
|
+
"tap_to_start": "탭하거나 Enter",
|
|
49
|
+
"target": "타깃: {target}",
|
|
50
|
+
"target_availability_summary": "타깃 {target}: {summary}",
|
|
51
|
+
"target_config_unavailable": "타깃 설정: 없음",
|
|
52
|
+
"target_feature_iap": "결제",
|
|
53
|
+
"target_feature_interstitial_ads": "전면광고",
|
|
54
|
+
"target_feature_leaderboard": "랭킹",
|
|
55
|
+
"target_feature_localization": "다국어",
|
|
56
|
+
"target_feature_rewarded_ads": "보상광고"
|
|
57
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mpgd/i18n",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Paraglide-backed message catalog for mpgd demos and target-aware UI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/imjlk/mpgd-kit.git",
|
|
9
|
+
"directory": "packages/i18n"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/imjlk/mpgd-kit/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/imjlk/mpgd-kit#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mpgd",
|
|
17
|
+
"phaser",
|
|
18
|
+
"vite",
|
|
19
|
+
"typescript",
|
|
20
|
+
"capacitor",
|
|
21
|
+
"apps-in-toss",
|
|
22
|
+
"game-development",
|
|
23
|
+
"game-distribution"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./messages": {
|
|
33
|
+
"types": "./dist/paraglide/messages.d.ts",
|
|
34
|
+
"default": "./dist/paraglide/messages.js"
|
|
35
|
+
},
|
|
36
|
+
"./runtime": {
|
|
37
|
+
"types": "./dist/paraglide/runtime.d.ts",
|
|
38
|
+
"default": "./dist/paraglide/runtime.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"messages",
|
|
44
|
+
"project.inlang"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@mpgd/platform": "0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@inlang/paraglide-js": "2.20.2",
|
|
51
|
+
"@inlang/plugin-message-format": "4.4.0",
|
|
52
|
+
"@types/node": "^24.0.0",
|
|
53
|
+
"ttsc": "0.16.9",
|
|
54
|
+
"typescript": "5.9.3"
|
|
55
|
+
},
|
|
56
|
+
"main": "./dist/index.js",
|
|
57
|
+
"types": "./dist/index.d.ts",
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"paraglide:build": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide --emit-ts-declarations",
|
|
63
|
+
"check": "pnpm paraglide:build && ttsc --noEmit",
|
|
64
|
+
"lint": "pnpm check",
|
|
65
|
+
"format": "ttsc format",
|
|
66
|
+
"fix": "ttsc fix",
|
|
67
|
+
"test": "pnpm paraglide:build && cd ../.. && pnpm exec ttsx --no-plugins --project tsconfig.graph.json packages/i18n/test/i18n-smoke.ts"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://inlang.com/schema/project-settings",
|
|
3
|
+
"baseLocale": "en",
|
|
4
|
+
"locales": ["en", "ko"],
|
|
5
|
+
"modules": ["./node_modules/@inlang/plugin-message-format/dist/index.js"],
|
|
6
|
+
"plugin.inlang.messageFormat": {
|
|
7
|
+
"pathPattern": "./messages/{locale}.json",
|
|
8
|
+
"sort": "asc"
|
|
9
|
+
}
|
|
10
|
+
}
|