Skip to content

Commit f7e486b

Browse files
authored
feat: handle i18n public resources not redirect (#7862)
1 parent c369a2e commit f7e486b

File tree

4 files changed

+56
-1
lines changed

4 files changed

+56
-1
lines changed

packages/runtime/plugin-i18n/src/cli/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,24 @@ export const i18nPlugin = (
3131
});
3232

3333
api._internalServerPlugins(({ plugins }) => {
34+
const { serverRoutes } = api.getAppContext();
35+
36+
let staticRoutePrefixes: string[] = [];
37+
if (serverRoutes && Array.isArray(serverRoutes)) {
38+
// Get static route prefixes from 'public' directories
39+
// 'public' routes are handled by static plugin
40+
staticRoutePrefixes = serverRoutes
41+
.filter(
42+
route => !route.entryName && route.entryPath.startsWith('public'),
43+
)
44+
.map(route => route.urlPath)
45+
.filter(Boolean);
46+
}
3447
plugins.push({
3548
name: '@modern-js/plugin-i18n/server',
3649
options: {
3750
localeDetection,
51+
staticRoutePrefixes,
3852
},
3953
});
4054
return { plugins };

packages/runtime/plugin-i18n/src/server/index.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getLocaleDetectionOptions } from '../shared/utils.js';
1010

1111
export interface I18nPluginOptions {
1212
localeDetection: LocaleDetectionOptions;
13+
staticRoutePrefixes: string[];
1314
}
1415

1516
/**
@@ -77,6 +78,18 @@ const convertToHonoLanguageDetectorOptions = (
7778
};
7879
};
7980

81+
/**
82+
* Check if the given pathname is a static resource request
83+
*/
84+
const isStaticResourceRequest = (
85+
pathname: string,
86+
staticRoutePrefixes: string[],
87+
): boolean => {
88+
return staticRoutePrefixes.some(
89+
prefix => pathname.startsWith(`${prefix}/`) || pathname === prefix,
90+
);
91+
};
92+
8093
const getLanguageFromPath = (
8194
req: any,
8295
urlPath: string,
@@ -156,6 +169,7 @@ export const i18nServerPlugin = (options: I18nPluginOptions): ServerPlugin => ({
156169
fallbackLanguage = 'en',
157170
detection,
158171
} = getLocaleDetectionOptions(entryName, options.localeDetection);
172+
const staticRoutePrefixes = options.staticRoutePrefixes;
159173
const originUrlPath = route.urlPath;
160174
const urlPath = originUrlPath.endsWith('/')
161175
? `${originUrlPath}*`
@@ -168,17 +182,36 @@ export const i18nServerPlugin = (options: I18nPluginOptions): ServerPlugin => ({
168182
fallbackLanguage,
169183
detection,
170184
);
185+
const detectorHandler = languageDetector(detectorOptions);
171186
middlewares.push({
172187
name: 'i18n-language-detector',
173188
path: urlPath,
174-
handler: languageDetector(detectorOptions),
189+
handler: async (c: Context, next: Next) => {
190+
const url = new URL(c.req.url);
191+
const pathname = url.pathname;
192+
193+
// For static resource requests, skip language detection
194+
if (isStaticResourceRequest(pathname, staticRoutePrefixes)) {
195+
return await next();
196+
}
197+
198+
return detectorHandler(c, next);
199+
},
175200
});
176201
}
177202

178203
middlewares.push({
179204
name: 'i18n-server-middleware',
180205
path: urlPath,
181206
handler: async (c: Context, next: Next) => {
207+
const url = new URL(c.req.url);
208+
const pathname = url.pathname;
209+
210+
// For static resource requests, skip i18n processing
211+
if (isStaticResourceRequest(pathname, staticRoutePrefixes)) {
212+
return await next();
213+
}
214+
182215
const language = getLanguageFromPath(c.req, urlPath, languages);
183216
if (!language) {
184217
// Get detected language from languageDetector middleware
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test public

tests/integration/i18n/app-ssr/tests/index.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,11 @@ describe('app-ssr-i18n', () => {
8787
{ timeout: 10000 },
8888
);
8989
});
90+
test('static-resource', async () => {
91+
const response = await page.goto(`http://localhost:${appPort}/text.txt`, {
92+
waitUntil: ['networkidle0'],
93+
});
94+
const body = await response?.text();
95+
expect(body).toContain('test public');
96+
});
9097
});

0 commit comments

Comments
 (0)