-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
7 changed files
with
219 additions
and
5 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import {AppContext} from '@gravity-ui/nodekit'; | ||
import acceptLanguage from 'accept-language-parser'; | ||
import type {Express} from 'express'; | ||
import {setLang} from './set-lang'; | ||
|
||
export function setupLangMiddleware(appCtx: AppContext, expressApp: Express) { | ||
const config = appCtx.config; | ||
|
||
const {appDefaultLang, appAllowedLangs, appLangQueryParamName} = config; | ||
if (!(appAllowedLangs && appAllowedLangs.length > 0 && appDefaultLang)) { | ||
return; | ||
} | ||
expressApp.use((req, _res, next) => { | ||
const langQuery = appLangQueryParamName && req.query[appLangQueryParamName]; | ||
if (langQuery && typeof langQuery === 'string' && appAllowedLangs.includes(langQuery)) { | ||
setLang({lang: langQuery, ctx: req.ctx}); | ||
return next(); | ||
} | ||
|
||
setLang({lang: appDefaultLang, ctx: req.ctx}); | ||
|
||
if (config.appGetLangByHostname) { | ||
const langByHostname = config.appGetLangByHostname(req.hostname); | ||
|
||
if (langByHostname) { | ||
setLang({lang: langByHostname, ctx: req.ctx}); | ||
} | ||
} else { | ||
const tld = req.hostname.split('.').pop(); | ||
const langByTld = tld && config.appLangByTld ? config.appLangByTld[tld] : undefined; | ||
|
||
if (langByTld) { | ||
setLang({lang: langByTld, ctx: req.ctx}); | ||
} | ||
} | ||
|
||
if (req.headers['accept-language']) { | ||
const langByHeader = acceptLanguage.pick( | ||
appAllowedLangs, | ||
req.headers['accept-language'], | ||
{ | ||
loose: true, | ||
}, | ||
); | ||
|
||
if (langByHeader) { | ||
setLang({lang: langByHeader, ctx: req.ctx}); | ||
} | ||
} | ||
|
||
return next(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type {AppContext} from '@gravity-ui/nodekit'; | ||
import {USER_LANGUAGE_PARAM_NAME} from '@gravity-ui/nodekit'; | ||
|
||
export const setLang = ({lang, ctx}: {lang: string; ctx: AppContext}) => { | ||
const config = ctx.config; | ||
if (!config.appAllowedLangs || config.appAllowedLangs.includes(lang)) { | ||
ctx.set(USER_LANGUAGE_PARAM_NAME, lang); | ||
return true; | ||
} | ||
|
||
return false; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import {ExpressKit, Request, Response} from '..'; | ||
import {NodeKit, USER_LANGUAGE_PARAM_NAME} from '@gravity-ui/nodekit'; | ||
import request from 'supertest'; | ||
|
||
const setupApp = (langConfig: NodeKit['config'] = {}) => { | ||
const nodekit = new NodeKit({ | ||
config: { | ||
appDefaultLang: 'ru', | ||
appAllowedLangs: ['ru', 'en'], | ||
...langConfig, | ||
}, | ||
}); | ||
const routes = { | ||
'GET /test': { | ||
handler: (req: Request, res: Response) => { | ||
res.status(200); | ||
const lang = req.ctx.get(USER_LANGUAGE_PARAM_NAME); | ||
res.send({lang}); | ||
}, | ||
}, | ||
}; | ||
|
||
const app = new ExpressKit(nodekit, routes); | ||
|
||
return app; | ||
}; | ||
|
||
describe('langMiddleware with default options', () => { | ||
it('should set default lang if no hostname or accept-language header', async () => { | ||
const app = setupApp(); | ||
const res = await request.agent(app.express).get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"ru"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
|
||
it('should set lang for en domains by tld', async () => { | ||
const app = setupApp({appLangByTld: {com: 'en', ru: 'ru'}}); | ||
const res = await request.agent(app.express).host('www.foo.com').get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"en"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
|
||
it('should set lang for ru domains by tld ', async () => { | ||
const app = setupApp({appLangByTld: {com: 'en', ru: 'ru'}}); | ||
const res = await request.agent(app.express).host('www.foo.ru').get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"ru"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
|
||
it('should set default lang for other domains by tld ', async () => { | ||
const app = setupApp({appLangByTld: {com: 'en', ru: 'ru'}}); | ||
const res = await request.agent(app.express).host('www.foo.jp').get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"ru"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
}); | ||
|
||
describe('langMiddleware with getLangByHostname is set', () => { | ||
it('should set lang by known hostname if getLangByHostname is set', async () => { | ||
const app = setupApp({ | ||
appGetLangByHostname: (hostname) => (hostname === 'www.foo.com' ? 'en' : undefined), | ||
}); | ||
const res = await request.agent(app.express).host('www.foo.com').get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"en"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
it("shouldn't set default lang for unknown hostname if getLangByHostname is set", async () => { | ||
const app = setupApp({ | ||
appGetLangByHostname: (hostname) => (hostname === 'www.foo.com' ? 'en' : undefined), | ||
}); | ||
const res = await request.agent(app.express).host('www.bar.com').get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"ru"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
}); | ||
|
||
describe('langMiddleware with accept-language header', () => { | ||
it('should set lang if known accept-language', async () => { | ||
const app = setupApp({ | ||
appGetLangByHostname: (hostname) => (hostname === 'www.foo.com' ? 'en' : undefined), | ||
}); | ||
const res = await request | ||
.agent(app.express) | ||
.host('www.foo.com') | ||
.set('accept-language', 'ru-RU, ru;q=0.9, en-US;q=0.8, en;q=0.7, fr;q=0.6') | ||
.get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"ru"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
it('should set tld lang for unknown accept-language', async () => { | ||
const app = setupApp({ | ||
appGetLangByHostname: (hostname) => (hostname === 'www.foo.com' ? 'en' : undefined), | ||
}); | ||
const res = await request | ||
.agent(app.express) | ||
.host('www.foo.com') | ||
.set('accept-language', 'fr;q=0.6') | ||
.get('/test'); | ||
|
||
expect(res.text).toBe('{"lang":"en"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
}); | ||
|
||
describe('langMiddleware with lang query param', () => { | ||
it('should set lang if known accept-language', async () => { | ||
const app = setupApp({ | ||
appLangQueryParamName: '_lang', | ||
}); | ||
const res = await request | ||
.agent(app.express) | ||
.host('www.foo.com') | ||
.set('accept-language', 'ru-RU, ru;q=0.9, en-US;q=0.8, en;q=0.7, fr;q=0.6') | ||
.get('/test?_lang=en'); | ||
|
||
expect(res.text).toBe('{"lang":"en"}'); | ||
expect(res.status).toBe(200); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters