1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | 'use strict'
|
30 |
|
31 | const debug = require('debug')('volebo:express:mw:lang')
|
32 | const _ = require('lodash')
|
33 | const express = require('express')
|
34 | const deepFreeze = require('deep-freeze')
|
35 |
|
36 | const urlBuilder = require('url').parse
|
37 |
|
38 | const knownLangs = require('./known-langs')
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | const LangMw = function(options) {
|
58 |
|
59 | const _opt = options || {}
|
60 |
|
61 | let defLangCode = _.toString(_opt.defaultLanguage) || 'en'
|
62 | const defLangData = knownLangs[_.toLower(defLangCode)]
|
63 |
|
64 | if (!defLangData) {
|
65 | throw new Error(`Unknown defaultLanguage: [${defLangCode}]`)
|
66 | }
|
67 |
|
68 | defLangCode = defLangData.code
|
69 |
|
70 | const available_lang_codes = _opt.availableLanguages || []
|
71 | available_lang_codes.push(defLangCode)
|
72 |
|
73 | const _available = _(available_lang_codes)
|
74 | .uniq()
|
75 | .map(lcode => knownLangs[_.toLower(lcode)])
|
76 | .filter()
|
77 | .value()
|
78 | const availableFrozen = deepFreeze(_available)
|
79 | const _availableDict = _.keyBy(availableFrozen, v => _.toLower(v.code))
|
80 |
|
81 | const _onLangDetected = _opt.onLangDetected || _.noop
|
82 |
|
83 | if (!_.isFunction(_onLangDetected)) {
|
84 | throw new TypeError('options.onLangDetected MUST be a function')
|
85 | }
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | const mw_lang_pre_handler = function mw_lang_pre_handler(req, res, next) {
|
94 | let lang_code = _.get(req.params, 'lang')
|
95 | let is_defLangCode
|
96 |
|
97 | if (_.isNil(lang_code)) {
|
98 | is_defLangCode = true
|
99 | lang_code = defLangCode
|
100 | } else {
|
101 | is_defLangCode = false
|
102 | const cult_code = _.get(req.params, 'cult', '')
|
103 | lang_code = lang_code + cult_code
|
104 | }
|
105 |
|
106 | let lc = _availableDict[_.toLower(lang_code)]
|
107 |
|
108 | if (!lc){
|
109 |
|
110 |
|
111 | const _urlPathname = urlBuilder(req.originalUrl).pathname
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 | const _prependSlash = '/' !== _urlPathname[_urlPathname.length-1]
|
123 |
|
124 | const _newUrl = _prependSlash ? '/' + req.originalUrl : req.originalUrl
|
125 | debug('use default culture and restore URL to original value [%s]', _newUrl)
|
126 | req.url = _newUrl
|
127 |
|
128 | lc = defLangData
|
129 | is_defLangCode = true
|
130 | }
|
131 |
|
132 | const localeinfo = {}
|
133 |
|
134 | localeinfo.defaultLanguage = defLangCode
|
135 | localeinfo.available = availableFrozen
|
136 | localeinfo.usingDefault = is_defLangCode
|
137 |
|
138 | localeinfo.routeTo = function _routeTo(local_path, explicit_lang_code) {
|
139 | if (_.isNil(explicit_lang_code)) {
|
140 | explicit_lang_code = localeinfo.code
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 | const lang = _availableDict[_.toLower(explicit_lang_code)]
|
146 | const effective_lang_code = lang ? lang.code : defLangCode
|
147 |
|
148 | let p = local_path
|
149 | if (effective_lang_code !== defLangCode) {
|
150 | p = `/${effective_lang_code}${local_path}`
|
151 | }
|
152 |
|
153 | return p
|
154 | }
|
155 |
|
156 | localeinfo.setLocale = function _setLocale(lc) {
|
157 |
|
158 | const new_lc = _availableDict[_.toLower(lc)]
|
159 |
|
160 | if (!new_lc) {
|
161 | throw new Error(`Locale not found: ${lc}`)
|
162 | }
|
163 |
|
164 | _.assign(this, new_lc)
|
165 |
|
166 | _onLangDetected(new_lc.code, req, res)
|
167 | }
|
168 |
|
169 |
|
170 | res.locals.lang = localeinfo
|
171 | req.lang = localeinfo
|
172 |
|
173 | localeinfo.setLocale(lc.code)
|
174 |
|
175 | return next()
|
176 | }
|
177 |
|
178 | const _router = new express.Router()
|
179 | _router.available = availableFrozen
|
180 | _router.defaultLanguage = defLangCode
|
181 |
|
182 | _router.esu = function(app) {
|
183 |
|
184 | app.use('/:lang(\\w{2})?:cult([-_]\\w{2,3})?', mw_lang_pre_handler, _router)
|
185 | }
|
186 |
|
187 | return _router
|
188 | }
|
189 |
|
190 | exports = module.exports = LangMw
|