module description import boom from "@hapi/boom" ; import fs from "fs" ; import path from "path" ; import lodash from "lodash" ; import headerParser from "accept-language-parser" ; import Joi from "joi" ; import pkg from "../../../package.json" ; typedef PluginOptions property { Array . < string > } locales property { string | null } default property { string | null } configFile property { string | null } configKey property scan property { string } scan . path property { string } scan . fileTypes property { boolean } scan . directories property { Array . < string > } scan . exclude property { string | null } param property { string | null } query property { string | null } cookie property { string | null } cookieKey property { string | null } header property { Array . < string > } order property { boolean } throw404 property { string | null } getter property { string | null } setter property { string | null } attribute property { boolean } createAccessors property { string } onEvent type { PluginOptions } private var defaultOptions = { locales : [ ] , configFile : "" , configKey : "" , default : null , scan : { path : null , fileType : "json" , directories : true , exclude : [ "templates" , "template.json" ] , } , param : "lang" , query : "lang" , cookie : "lang" , cookieKey : "lang" , header : "accept-language" , order : [ "query" , "cookie" , "params" , "headers" ] , throw404 : true , getter : "i18n.getLocale" , setter : "i18n.setLocale" , attribute : "i18n.locale" , createAccessors : true , onEvent : "onPreAuth" , } ; var orderParameters = { params : "parseParam" , query : "parseQuery" , headers : "parseHeader" , cookie : "parseCookie" , } ; var optionsSchema = Joi . object ( { locales : Joi . array ( ) . items ( Joi . string ( ) ) . default ( defaultOptions . locales ) , default : Joi . string ( ) . allow ( null ) . default ( defaultOptions . default ) , configFile : Joi . string ( ) . allow ( null ) . default ( defaultOptions . configFile ) , configKey : Joi . string ( ) . allow ( null ) . default ( defaultOptions . configKey ) , scan : Joi . object ( { path : Joi . string ( ) . default ( defaultOptions . scan . path ) , fileType : Joi . string ( ) . default ( defaultOptions . scan . fileType ) , directories : Joi . boolean ( ) . default ( defaultOptions . scan . directories ) , exclude : Joi . array ( ) . items ( Joi . string ( ) ) . allow ( null ) . default ( defaultOptions . scan . exclude ) , } ) . allow ( null ) . default ( defaultOptions . scan ) , param : Joi . string ( ) . allow ( null ) . default ( defaultOptions . param ) , query : Joi . string ( ) . allow ( null ) . default ( defaultOptions . query ) , cookie : Joi . string ( ) . allow ( null ) . default ( defaultOptions . cookie ) , cookieKey : Joi . string ( ) . allow ( null ) . default ( defaultOptions . cookieKey ) , header : Joi . string ( ) . allow ( null ) . default ( defaultOptions . header ) , order : Joi . array ( ) . items ( Joi . any ( ) . valid ( ... Object . keys ( orderParameters ) ) ) . default ( defaultOptions . order ) , throw404 : Joi . boolean ( ) . default ( defaultOptions . throw404 ) , getter : Joi . string ( ) . allow ( null ) . default ( defaultOptions . getter ) , setter : Joi . string ( ) . allow ( null ) . default ( defaultOptions . setter ) , attribute : Joi . string ( ) . allow ( null ) . default ( defaultOptions . attribute ) , createAccessors : Joi . string ( ) . allow ( null ) . default ( defaultOptions . createAccessors ) , onEvent : Joi . string ( ) . default ( defaultOptions . onEvent ) , } ) ; param { PluginOptions } options constructor private function Internal ( options ) { if ( ( options . setter && options . setter . indexOf ( "." ) > - 1 ) || ( options . getter && options . getter . indexOf ( "." ) > - 1 ) ) { throw new Error ( "Getter (" + options . getter + ") and setter (" + options . setter + ") methods cannot be nested, so they cannot contain dot(.)" ) ; } this . options = Joi . attempt ( options , optionsSchema ) ; } param { Object } request returns { string | undefined } private Internal . prototype . parseParam = function parseParam ( request ) { "use strict" ; if ( ! request . params . hasOwnProperty ( this . options . param ) ) return ; var name = this . options . param ; var locales = lodash . get ( request . params , name ) ; var match = this . bestMatch ( locales ) ; if ( ! match && this . options . throw404 ) { throw new Error ( "Requested locale/language " + locales + " cannot be found." ) ; } return match ; } ; param { Object } request returns { string | undefined } private Internal . prototype . parseQuery = function parseQuery ( request ) { "use strict" ; var name = this . options . query ; var locales = lodash . get ( request . query , name ) ; return this . bestMatch ( locales ) ; } ; param { Object } request returns { string | undefined } private Internal . prototype . parseCookie = function parseCookie ( request ) { "use strict" ; var name = this . options . cookie ; var key = this . options . cookieKey ; var locales = lodash . get ( request . state [ name ] , key ) ; return this . bestMatch ( locales ) ; } ; param { Object } request returns { string | undefined } private Internal . prototype . parseHeader = function parseHeader ( request ) { var name = this . options . header ; var raw = headerParser . parse ( request . headers [ name ] ) ; var locales = raw . map ( function ( value ) { return value . region ? value . code + "_" + value . region : value . code ; } ) ; return this . bestMatch ( locales ) ; } ; param { string | Array . < string > } requested returns { string | undefined } private Internal . prototype . bestMatch = function bestMatch ( requested ) { if ( ! requested ) return ; if ( ! Array . isArray ( requested ) ) requested = [ requested ] ; for ( const one of requested ) { if ( this . locales . indexOf ( one ) > - 1 ) return one ; } } ; param { string } path param { boolean } shouldBeDir returns { boolean } private function fileExists ( path , shouldBeDir ) { "use strict" ; try { var lstat = fs . lstatSync ( path ) ; if ( shouldBeDir && lstat . isDirectory ( ) ) { return true ; } if ( ! shouldBeDir && lstat . isFile ( ) ) { return true ; } } catch ( err ) { return false ; } return false ; } returns { Array . < string > } throws private Internal . prototype . scan = function scan ( ) { if ( this . options . scan && ! fileExists ( this . options . scan . path , true ) ) { throw new Error ( 'Locales directory "' + this . options . scan . path + '"cannot be found.' ) ; } const dir = this . options . scan . path ; const files = fs . readdirSync ( dir ) ; const locales = [ ] ; for ( const file of files ) { const fullPath = path . join ( dir , file ) ; if ( this . options . scan . exclude . indexOf ( file ) > - 1 || ( fs . statSync ( fullPath ) . isDirectory ( ) && ! this . options . scan . directories ) ) { continue ; } locales . push ( path . basename ( file , path . extname ( file ) ) ) ; } return lodash . uniq ( locales ) ; } ; returns { Array | null } throws private param { Object } request returns { string | undefined } private Internal . prototype . determineLocale = function determineLocale ( request ) { let requestedLocale ; for ( const method of this . options . order ) { requestedLocale = this [ orderParameters [ method ] ] ( request ) ; if ( requestedLocale ) break ; } return requestedLocale || this . default ; } ; param { Object } request param { Function } reply returns { * } Internal . prototype . processRequest = function processRequest ( request , h ) { "use strict" ; try { var locale = this . determineLocale ( request ) ; } catch ( err ) { } const getter = this . options . getter ; const setter = this . options . setter ; const attribute = this . options . attribute ; if ( this . options . createAccessors ) { if ( ! lodash . get ( request , getter ) ) { lodash . set ( request , getter , function ( ) { return lodash . get ( request , attribute ) ; } ) ; } if ( ! lodash . get ( request , setter ) ) { lodash . set ( request , setter , function ( locale ) { return lodash . set ( request , attribute , locale ) ; } ) ; } } lodash . get ( request , setter ) ( locale ) ; return h . continue ; } ; const plugin = { name : "locale" , version : pkg . version , pkg : pkg , once : true , multiple : false , param { Object } server param { PluginOptions } options register : async function ( server , options ) { try { var internal = new Internal ( options ) ; } catch ( err ) { throw new boom . Boom ( err ) ; } module description example name function returns { Array . < string > } example server . expose ( "getLocales" , function getLocales ( ) { return internal . locales ; } ) ; name function returns { string } server . expose ( "getDefaultLocale" , function getDefaultLocale ( ) { return internal . default ; } ) ; name function param { Object } request returns { string } server . expose ( "getLocale" , function getLocale ( request ) { try { return lodash . get ( request , internal . options . getter ) ( ) ; } catch ( err ) { return null ; } } ) ; server . ext ( internal . options . onEvent , internal . processRequest , { bind : internal , } ) ; } , } ; export default plugin ;