| let shim; | 
| class Y18N { | 
|     constructor(opts) { | 
|         // configurable options. | 
|         opts = opts || {}; | 
|         this.directory = opts.directory || './locales'; | 
|         this.updateFiles = typeof opts.updateFiles === 'boolean' ? opts.updateFiles : true; | 
|         this.locale = opts.locale || 'en'; | 
|         this.fallbackToLanguage = typeof opts.fallbackToLanguage === 'boolean' ? opts.fallbackToLanguage : true; | 
|         // internal stuff. | 
|         this.cache = Object.create(null); | 
|         this.writeQueue = []; | 
|     } | 
|     __(...args) { | 
|         if (typeof arguments[0] !== 'string') { | 
|             return this._taggedLiteral(arguments[0], ...arguments); | 
|         } | 
|         const str = args.shift(); | 
|         let cb = function () { }; // start with noop. | 
|         if (typeof args[args.length - 1] === 'function') | 
|             cb = args.pop(); | 
|         cb = cb || function () { }; // noop. | 
|         if (!this.cache[this.locale]) | 
|             this._readLocaleFile(); | 
|         // we've observed a new string, update the language file. | 
|         if (!this.cache[this.locale][str] && this.updateFiles) { | 
|             this.cache[this.locale][str] = str; | 
|             // include the current directory and locale, | 
|             // since these values could change before the | 
|             // write is performed. | 
|             this._enqueueWrite({ | 
|                 directory: this.directory, | 
|                 locale: this.locale, | 
|                 cb | 
|             }); | 
|         } | 
|         else { | 
|             cb(); | 
|         } | 
|         return shim.format.apply(shim.format, [this.cache[this.locale][str] || str].concat(args)); | 
|     } | 
|     __n() { | 
|         const args = Array.prototype.slice.call(arguments); | 
|         const singular = args.shift(); | 
|         const plural = args.shift(); | 
|         const quantity = args.shift(); | 
|         let cb = function () { }; // start with noop. | 
|         if (typeof args[args.length - 1] === 'function') | 
|             cb = args.pop(); | 
|         if (!this.cache[this.locale]) | 
|             this._readLocaleFile(); | 
|         let str = quantity === 1 ? singular : plural; | 
|         if (this.cache[this.locale][singular]) { | 
|             const entry = this.cache[this.locale][singular]; | 
|             str = entry[quantity === 1 ? 'one' : 'other']; | 
|         } | 
|         // we've observed a new string, update the language file. | 
|         if (!this.cache[this.locale][singular] && this.updateFiles) { | 
|             this.cache[this.locale][singular] = { | 
|                 one: singular, | 
|                 other: plural | 
|             }; | 
|             // include the current directory and locale, | 
|             // since these values could change before the | 
|             // write is performed. | 
|             this._enqueueWrite({ | 
|                 directory: this.directory, | 
|                 locale: this.locale, | 
|                 cb | 
|             }); | 
|         } | 
|         else { | 
|             cb(); | 
|         } | 
|         // if a %d placeholder is provided, add quantity | 
|         // to the arguments expanded by util.format. | 
|         const values = [str]; | 
|         if (~str.indexOf('%d')) | 
|             values.push(quantity); | 
|         return shim.format.apply(shim.format, values.concat(args)); | 
|     } | 
|     setLocale(locale) { | 
|         this.locale = locale; | 
|     } | 
|     getLocale() { | 
|         return this.locale; | 
|     } | 
|     updateLocale(obj) { | 
|         if (!this.cache[this.locale]) | 
|             this._readLocaleFile(); | 
|         for (const key in obj) { | 
|             if (Object.prototype.hasOwnProperty.call(obj, key)) { | 
|                 this.cache[this.locale][key] = obj[key]; | 
|             } | 
|         } | 
|     } | 
|     _taggedLiteral(parts, ...args) { | 
|         let str = ''; | 
|         parts.forEach(function (part, i) { | 
|             const arg = args[i + 1]; | 
|             str += part; | 
|             if (typeof arg !== 'undefined') { | 
|                 str += '%s'; | 
|             } | 
|         }); | 
|         return this.__.apply(this, [str].concat([].slice.call(args, 1))); | 
|     } | 
|     _enqueueWrite(work) { | 
|         this.writeQueue.push(work); | 
|         if (this.writeQueue.length === 1) | 
|             this._processWriteQueue(); | 
|     } | 
|     _processWriteQueue() { | 
|         const _this = this; | 
|         const work = this.writeQueue[0]; | 
|         // destructure the enqueued work. | 
|         const directory = work.directory; | 
|         const locale = work.locale; | 
|         const cb = work.cb; | 
|         const languageFile = this._resolveLocaleFile(directory, locale); | 
|         const serializedLocale = JSON.stringify(this.cache[locale], null, 2); | 
|         shim.fs.writeFile(languageFile, serializedLocale, 'utf-8', function (err) { | 
|             _this.writeQueue.shift(); | 
|             if (_this.writeQueue.length > 0) | 
|                 _this._processWriteQueue(); | 
|             cb(err); | 
|         }); | 
|     } | 
|     _readLocaleFile() { | 
|         let localeLookup = {}; | 
|         const languageFile = this._resolveLocaleFile(this.directory, this.locale); | 
|         try { | 
|             // When using a bundler such as webpack, readFileSync may not be defined: | 
|             if (shim.fs.readFileSync) { | 
|                 localeLookup = JSON.parse(shim.fs.readFileSync(languageFile, 'utf-8')); | 
|             } | 
|         } | 
|         catch (err) { | 
|             if (err instanceof SyntaxError) { | 
|                 err.message = 'syntax error in ' + languageFile; | 
|             } | 
|             if (err.code === 'ENOENT') | 
|                 localeLookup = {}; | 
|             else | 
|                 throw err; | 
|         } | 
|         this.cache[this.locale] = localeLookup; | 
|     } | 
|     _resolveLocaleFile(directory, locale) { | 
|         let file = shim.resolve(directory, './', locale + '.json'); | 
|         if (this.fallbackToLanguage && !this._fileExistsSync(file) && ~locale.lastIndexOf('_')) { | 
|             // attempt fallback to language only | 
|             const languageFile = shim.resolve(directory, './', locale.split('_')[0] + '.json'); | 
|             if (this._fileExistsSync(languageFile)) | 
|                 file = languageFile; | 
|         } | 
|         return file; | 
|     } | 
|     _fileExistsSync(file) { | 
|         return shim.exists(file); | 
|     } | 
| } | 
| export function y18n(opts, _shim) { | 
|     shim = _shim; | 
|     const y18n = new Y18N(opts); | 
|     return { | 
|         __: y18n.__.bind(y18n), | 
|         __n: y18n.__n.bind(y18n), | 
|         setLocale: y18n.setLocale.bind(y18n), | 
|         getLocale: y18n.getLocale.bind(y18n), | 
|         updateLocale: y18n.updateLocale.bind(y18n), | 
|         locale: y18n.locale | 
|     }; | 
| } |