next-i18next-middleware.ts 3.4 KB
Newer Older
P
Peter Pan 已提交
1 2 3 4 5 6 7 8 9 10 11
import {NextFunction, Request, Response} from 'express';
import {
    addSubpath,
    lngFromReq,
    redirectWithoutCache,
    removeSubpath,
    subpathFromLng,
    subpathIsPresent,
    subpathIsRequired
} from '../utils';

12
import NextI18Next from '../index';
13
import i18nextMiddleware from 'i18next-http-middleware';
14 15
import pathMatch from 'path-match';

P
Peter Pan 已提交
16 17
const route = pathMatch();

18
export default function (nexti18next: NextI18Next) {
P
Peter Pan 已提交
19
    const {config, i18n} = nexti18next;
20
    const {allLanguages, ignoreRoutes, localeSubpaths, publicPath} = config;
P
Peter Pan 已提交
21

22
    const isI18nRoute = (req: Request) => ignoreRoutes?.every(x => !req.url.startsWith((publicPath || '') + x));
P
Peter Pan 已提交
23 24 25 26 27
    const localeSubpathRoute = route(`/:subpath(${Object.values(localeSubpaths || {}).join('|')})(.*)`);

    const middleware = [];

    /*
P
Peter Pan 已提交
28 29 30 31
        If not using server side language detection,
        we need to manually set the language for
        each request
    */
P
Peter Pan 已提交
32 33 34 35 36 37 38 39 40 41
    if (!config.serverLanguageDetection) {
        middleware.push((req: Request, _res: Response, next: NextFunction) => {
            if (isI18nRoute(req)) {
                req.lng = config.defaultLanguage;
            }
            next();
        });
    }

    /*
P
Peter Pan 已提交
42 43
        This does the bulk of the i18next work
    */
P
Peter Pan 已提交
44 45 46
    middleware.push(i18nextMiddleware.handle(i18n));

    /*
P
Peter Pan 已提交
47 48
        This does the locale subpath work
    */
P
Peter Pan 已提交
49 50
    middleware.push((req: Request, res: Response, next: NextFunction) => {
        if (isI18nRoute(req) && req.i18n) {
51 52 53 54
            let url = publicPath ? req.url.replace(publicPath, '') : req.url;
            if (!url) {
                url = '/';
            }
P
Peter Pan 已提交
55 56 57
            let currentLng = lngFromReq(req);
            const currentLngSubpath = subpathFromLng(config, currentLng);
            const currentLngRequiresSubpath = subpathIsRequired(config, currentLng || '');
58
            const currentLngSubpathIsPresent = subpathIsPresent(url, currentLngSubpath);
P
Peter Pan 已提交
59 60

            const lngFromCurrentSubpath = allLanguages.find((l: string) =>
61
                subpathIsPresent(url, subpathFromLng(config, l))
P
Peter Pan 已提交
62 63 64 65
            );

            if (lngFromCurrentSubpath !== undefined && lngFromCurrentSubpath !== currentLng) {
                /*
P
Peter Pan 已提交
66 67 68 69
                    If a user has hit a subpath which does not
                    match their language, give preference to
                    the path, and change user language.
                */
P
Peter Pan 已提交
70 71 72 73
                req.i18n.changeLanguage(lngFromCurrentSubpath);
                currentLng = lngFromCurrentSubpath;
            } else if (currentLngRequiresSubpath && !currentLngSubpathIsPresent) {
                /*
P
Peter Pan 已提交
74 75 76
                    If a language subpath is required and
                    not present, prepend correct subpath
                */
77
                return redirectWithoutCache(res, (publicPath || '') + addSubpath(url, currentLngSubpath));
P
Peter Pan 已提交
78 79 80
            }

            /*
P
Peter Pan 已提交
81 82 83 84
                If a locale subpath is present in the URL,
                modify req.url in place so that NextJs will
                render the correct route
            */
P
Peter Pan 已提交
85
            if (typeof lngFromCurrentSubpath === 'string') {
86
                const params = localeSubpathRoute(url);
P
Peter Pan 已提交
87 88 89
                if (params !== false) {
                    const {subpath} = params;
                    req.query = {...req.query, subpath, lng: currentLng};
90
                    req.url = (publicPath || '') + removeSubpath(url, subpath);
P
Peter Pan 已提交
91 92 93 94 95 96 97 98 99
                }
            }
        }

        next();
    });

    return middleware;
}