1 | import {IncomingMessage, ServerResponse} from "http";
|
2 | import * as UrlParser from 'url';
|
3 |
|
4 | const SUB_DIR = /^(\/(\w\w(_\w\w)?))(?:\/|$)/;
|
5 | const SUB_DOMAIN_PIPE_REGEX = /(?:^|_)(\w\w(?:_\w\w)?)_(?:b\-[mt]\-)?[0-9a-f]{24,25}_pipe/i;
|
6 | const SUB_DOMAIN_REGEX = /^(?:www\.)?(\w\w(?:_\w\w)?)\./i;
|
7 |
|
8 | export interface ExtendedRequest extends IncomingMessage {
|
9 | originalUrl?: string;
|
10 |
|
11 | forceLocale?: string;
|
12 | bablic?:{
|
13 | locale: string;
|
14 | proxied?: boolean;
|
15 | }
|
16 | }
|
17 |
|
18 | export interface ExtendedResponse extends ServerResponse {
|
19 | locals?: any;
|
20 | }
|
21 |
|
22 | export type Middleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
|
23 |
|
24 | export interface KeywordMapper {
|
25 | [locale: string]:{
|
26 | [keyword:string]: string
|
27 | }
|
28 | }
|
29 |
|
30 | function getLocaleFromFolder(folderLocale, locales) {
|
31 | var index = locales.indexOf(folderLocale);
|
32 | if (index > -1)
|
33 | return locales[index];
|
34 | folderLocale = folderLocale.substr(0, 2);
|
35 | for (var i = 0; i < locales.length; i++) {
|
36 | if (locales[i].substr(0, 2) == folderLocale)
|
37 | return locales[i];
|
38 | }
|
39 | return '';
|
40 | }
|
41 |
|
42 |
|
43 | export function getLocaleByURL(parsedUrl, locale_detection, localeConfigs, cookieLocale, siteDefaultLocale, detectedLocale, isProxy, explicitLocale, subDirBase, folders, locales) {
|
44 | switch (locale_detection) {
|
45 | case 'querystring':
|
46 | if (parsedUrl.query && typeof(parsedUrl.query) == 'object')
|
47 | return parsedUrl.query.locale;
|
48 | var matches = /locale=([^&]+)/.exec(parsedUrl.query || '');
|
49 | if (matches && matches.length == 2 && matches[1])
|
50 | return matches[1];
|
51 | return cookieLocale || detectedLocale || siteDefaultLocale;
|
52 | case "subdomain":
|
53 | var regex = isProxy ? SUB_DOMAIN_PIPE_REGEX : SUB_DOMAIN_REGEX;
|
54 | var matches = regex.exec(parsedUrl.hostname);
|
55 | if (matches && matches.length > 1 && matches[1])
|
56 | return matches[1];
|
57 | return siteDefaultLocale;
|
58 | case "subdir":
|
59 | var pathname = parsedUrl.pathname;
|
60 | if (subDirBase)
|
61 | pathname = pathname.replace(subDirBase, '');
|
62 | var match = SUB_DIR.exec(pathname);
|
63 | if (match) {
|
64 | if (folders && locales && folders[match[2]])
|
65 | return getLocaleFromFolder(folders[match[2]], locales) || siteDefaultLocale;
|
66 | return match[2];
|
67 | }
|
68 | if (explicitLocale)
|
69 | return explicitLocale;
|
70 | if (cookieLocale)
|
71 | return cookieLocale;
|
72 | return detectedLocale || siteDefaultLocale;
|
73 | case "tld":
|
74 | var matches = /\.(\w\w)$/.exec(parsedUrl.hostname);
|
75 | if (matches && matches.length > 1 && matches[1])
|
76 | return matches[1];
|
77 | return null;
|
78 | case "custom":
|
79 | var parseDomainRule = function (str) {
|
80 | return RegExp((str + '').replace(/([.?+^$[\]\\(){}|-])/g, "\\$1").replace(/\*/g, '.*'), 'i');
|
81 | };
|
82 |
|
83 | for (var locale in localeConfigs) {
|
84 | var urlPattern = localeConfigs[locale];
|
85 | if (urlPattern && parseDomainRule(urlPattern).test(parsedUrl.href))
|
86 | return locale;
|
87 | }
|
88 | return explicitLocale || siteDefaultLocale;
|
89 | case "hash":
|
90 | var matches = /^#!locale=(\w\w(?:_\w\w)?)$/.exec(parsedUrl.hash || '');
|
91 | if (matches && matches.length > 1 && matches[1])
|
92 | return matches[1];
|
93 | return cookieLocale || detectedLocale || siteDefaultLocale;
|
94 | default:
|
95 | return cookieLocale;
|
96 | }
|
97 | }
|
98 | export interface SiteMeta{
|
99 | localeDetection: string;
|
100 | original: string;
|
101 | customUrls: {
|
102 | [locale:string]:string
|
103 | };
|
104 | default: string;
|
105 | autoDetect: boolean;
|
106 | localeKeys: string[];
|
107 | timestamp: number;
|
108 | includeQueryString: boolean;
|
109 | includeHash: boolean;
|
110 | singlePageApp: boolean;
|
111 | qsParams: string[],
|
112 | domain: string;
|
113 | mountSubs: string[];
|
114 | }
|
115 |
|
116 |
|
117 | export interface LastModifiedByLocale {
|
118 | [locale: string]: Date
|
119 | }
|
120 |
|
121 | export interface BablicLinkOptions {
|
122 | subDir?: boolean;
|
123 | subDirBase?: string;
|
124 | subDirOptional?: boolean;
|
125 | returnFull?: boolean;
|
126 | folders?:{[locale:string]:string}
|
127 | }
|
128 |
|
129 | export function getLink(locale: string, parsed: UrlParser.Url, meta: SiteMeta, options?: BablicLinkOptions) {
|
130 | options = options || {};
|
131 | let protocol = parsed.protocol || '';
|
132 | let hostname = parsed.hostname;
|
133 | let pathname = parsed.pathname || '/';
|
134 | let search = parsed.search || '';
|
135 | let hash = parsed.hash || '';
|
136 |
|
137 | let returnFull = options.returnFull && !!hostname;
|
138 | let localeDetection = meta.localeDetection;
|
139 | let original = meta.original;
|
140 | if(options.subDir)
|
141 | localeDetection = 'subdir';
|
142 | if(localeDetection == 'custom' && !(meta.customUrls && meta.customUrls[locale]))
|
143 | localeDetection = 'querystring';
|
144 |
|
145 | switch(localeDetection){
|
146 | case 'custom':
|
147 | let customUrl = meta.customUrls[locale];
|
148 | let confDomain = customUrl.indexOf('/') > -1 ? customUrl.substr(0,customUrl.indexOf('/')) : customUrl;
|
149 | return protocol + '//' + confDomain + pathname + search + hash;
|
150 |
|
151 | case 'querystring':
|
152 | if (/[?&]locale=([^&]+)/.test(search))
|
153 | search = search.replace(/([?&]locale=)([^&]+)/, '$1' + locale);
|
154 | else {
|
155 | if (search)
|
156 | search = search + '&locale=' + locale;
|
157 | else
|
158 | search = '?locale=' + locale;
|
159 | }
|
160 | if(returnFull)
|
161 | return protocol + '//' + hostname + pathname + search + hash;
|
162 |
|
163 | return pathname + search + hash;
|
164 |
|
165 | case 'subdir':
|
166 | if(options.subDirBase)
|
167 | pathname = pathname.replace(options.subDirBase,'');
|
168 | let match = SUB_DIR.exec(pathname);
|
169 | if (match) {
|
170 | pathname = pathname.substr(match[1].length);
|
171 | if (pathname.length == 0)
|
172 | pathname = '/';
|
173 | }
|
174 | let prefix = '';
|
175 | if(options.folders)
|
176 | prefix = '/' + getFolder(locale,options.folders);
|
177 | else if(locale != original)
|
178 | prefix = '/' + locale;
|
179 | if(options.subDirBase && (!options.subDirOptional || locale != original))
|
180 | prefix = options.subDirBase + prefix;
|
181 | if(returnFull)
|
182 | return protocol + '//' + hostname + prefix + pathname + search + hash;
|
183 | return prefix + pathname + search + hash;
|
184 | case 'hash':
|
185 | if(returnFull)
|
186 | return protocol + '//' + hostname + pathname + search + '#locale=' + locale;
|
187 | return '#locale_' + locale;
|
188 | }
|
189 | return `javascript:bablic.setLanguage("${locale}");`;
|
190 | }
|
191 |
|
192 |
|
193 | function getFolder(locale: string,folders:{[locale:string]:string}) :string{
|
194 | for(let folder in folders){
|
195 | if(folders[folder] == locale)
|
196 | return folder;
|
197 | }
|
198 | locale = locale.substr(0,2);
|
199 | for(let folder in folders){
|
200 | if(folders[folder].substr(0,2) == locale)
|
201 | return folder;
|
202 | }
|
203 | return locale;
|
204 | }
|