pax_global_header00006660000000000000000000000064140334001000014473gustar00rootroot0000000000000052 comment=58a9a3c715af18c5d2000eb0adb206cbbc28db82 y18n-5.0.8/000077500000000000000000000000001403340010000123045ustar00rootroot00000000000000y18n-5.0.8/.eslintrc000066400000000000000000000007101403340010000141260ustar00rootroot00000000000000{ "overrides": [ { "files": "*.ts", "parser": "@typescript-eslint/parser", "rules": { "no-unused-vars": "off", "no-useless-constructor": "off", "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-useless-constructor": "error" } } ], "parserOptions": { "ecmaVersion": 2017, "sourceType": "module" }, "plugins": [ "@typescript-eslint/eslint-plugin" ] } y18n-5.0.8/.github/000077500000000000000000000000001403340010000136445ustar00rootroot00000000000000y18n-5.0.8/.github/workflows/000077500000000000000000000000001403340010000157015ustar00rootroot00000000000000y18n-5.0.8/.github/workflows/ci.yaml000066400000000000000000000027011403340010000171600ustar00rootroot00000000000000name: ci on: push: branches: - master pull_request: types: [ assigned, opened, synchronize, reopened, labeled ] env: CI: true jobs: test: runs-on: ubuntu-latest strategy: matrix: node: [10.x, 12.x, 14.x] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: ${{ matrix.node }} - run: node --version - run: npm install - run: npm test windows: runs-on: windows-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: '12.x' - run: npm install - run: npm test coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: '14.x' - run: npm install - run: npm test - run: npm run coverage esm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: '14.x' - run: npm install - run: npm run test:esm deno: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install - run: npm run compile - uses: denolib/setup-deno@v2 - run: | deno --version deno test --allow-read test/deno/y18n-test.ts y18n-5.0.8/.github/workflows/release-please.yml000066400000000000000000000031161403340010000213140ustar00rootroot00000000000000on: push: branches: - master name: release-please jobs: release-please: runs-on: ubuntu-latest steps: - uses: bcoe/release-please-action@v2.5.5 id: release with: token: ${{ secrets.GITHUB_TOKEN }} release-type: node package-name: y18n - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install - run: npm run compile - name: push Deno release run: | git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com git remote add gh-token "https://${{ secrets.GITHUB_TOKEN}}@github.com/yargs/y18n.git" git checkout -b deno git add -f build git commit -a -m 'chore: ${{ steps.release.outputs.tag_name }} release' git push origin +deno git tag -a ${{ steps.release.outputs.tag_name }}-deno -m 'chore: ${{ steps.release.outputs.tag_name }} release' git push origin ${{ steps.release.outputs.tag_name }}-deno if: ${{ steps.release.outputs.release_created }} - uses: actions/setup-node@v1 with: node-version: 14 registry-url: 'https://external-dot-oss-automation.appspot.com/' if: ${{ steps.release.outputs.release_created }} - run: npm install if: ${{ steps.release.outputs.release_created }} - run: npm publish env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} if: ${{ steps.release.outputs.release_created }} y18n-5.0.8/.gitignore000066400000000000000000000001161403340010000142720ustar00rootroot00000000000000.DS_Store node_modules .nyc_output build coverage package-lock.json example.* y18n-5.0.8/.nycrc000066400000000000000000000002351403340010000134230ustar00rootroot00000000000000{ "exclude": [ "test/**" ], "reporter": [ "html", "text" ], "branches": 96.5, "lines": 100, "functions": 93, "statements": 100 } y18n-5.0.8/CHANGELOG.md000066400000000000000000000075071403340010000141260ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ### [5.0.8](https://www.github.com/yargs/y18n/compare/v5.0.7...v5.0.8) (2021-04-07) ### Bug Fixes * **deno:** force modern release for Deno ([b1c215a](https://www.github.com/yargs/y18n/commit/b1c215aed714bee5830e76de3e335504dc2c4dab)) ### [5.0.7](https://www.github.com/yargs/y18n/compare/v5.0.6...v5.0.7) (2021-04-07) ### Bug Fixes * **deno:** force release for deno ([#121](https://www.github.com/yargs/y18n/issues/121)) ([d3f2560](https://www.github.com/yargs/y18n/commit/d3f2560e6cedf2bfa2352e9eec044da53f9a06b2)) ### [5.0.6](https://www.github.com/yargs/y18n/compare/v5.0.5...v5.0.6) (2021-04-05) ### Bug Fixes * **webpack:** skip readFileSync if not defined ([#117](https://www.github.com/yargs/y18n/issues/117)) ([6966fa9](https://www.github.com/yargs/y18n/commit/6966fa91d2881cc6a6c531e836099e01f4da1616)) ### [5.0.5](https://www.github.com/yargs/y18n/compare/v5.0.4...v5.0.5) (2020-10-25) ### Bug Fixes * address prototype pollution issue ([#108](https://www.github.com/yargs/y18n/issues/108)) ([a9ac604](https://www.github.com/yargs/y18n/commit/a9ac604abf756dec9687be3843e2c93bfe581f25)) ### [5.0.4](https://www.github.com/yargs/y18n/compare/v5.0.3...v5.0.4) (2020-10-16) ### Bug Fixes * **exports:** node 13.0 and 13.1 require the dotted object form _with_ a string fallback ([#105](https://www.github.com/yargs/y18n/issues/105)) ([4f85d80](https://www.github.com/yargs/y18n/commit/4f85d80dbaae6d2c7899ae394f7ad97805df4886)) ### [5.0.3](https://www.github.com/yargs/y18n/compare/v5.0.2...v5.0.3) (2020-10-16) ### Bug Fixes * **exports:** node 13.0-13.6 require a string fallback ([#103](https://www.github.com/yargs/y18n/issues/103)) ([e39921e](https://www.github.com/yargs/y18n/commit/e39921e1017f88f5d8ea97ddea854ffe92d68e74)) ### [5.0.2](https://www.github.com/yargs/y18n/compare/v5.0.1...v5.0.2) (2020-10-01) ### Bug Fixes * **deno:** update types for deno ^1.4.0 ([#100](https://www.github.com/yargs/y18n/issues/100)) ([3834d9a](https://www.github.com/yargs/y18n/commit/3834d9ab1332f2937c935ada5e76623290efae81)) ### [5.0.1](https://www.github.com/yargs/y18n/compare/v5.0.0...v5.0.1) (2020-09-05) ### Bug Fixes * main had old index path ([#98](https://www.github.com/yargs/y18n/issues/98)) ([124f7b0](https://www.github.com/yargs/y18n/commit/124f7b047ba9596bdbdf64459988304e77f3de1b)) ## [5.0.0](https://www.github.com/yargs/y18n/compare/v4.0.0...v5.0.0) (2020-09-05) ### ⚠ BREAKING CHANGES * exports maps are now used, which modifies import behavior. * drops Node 6 and 4. begin following Node.js LTS schedule (#89) ### Features * add support for ESM and Deno [#95](https://www.github.com/yargs/y18n/issues/95)) ([4d7ae94](https://www.github.com/yargs/y18n/commit/4d7ae94bcb42e84164e2180366474b1cd321ed94)) ### Build System * drops Node 6 and 4. begin following Node.js LTS schedule ([#89](https://www.github.com/yargs/y18n/issues/89)) ([3cc0c28](https://www.github.com/yargs/y18n/commit/3cc0c287240727b84eaf1927f903612ec80f5e43)) ### 4.0.1 (2020-10-25) ### Bug Fixes * address prototype pollution issue ([#108](https://www.github.com/yargs/y18n/issues/108)) ([a9ac604](https://www.github.com/yargs/y18n/commit/7de58ca0d315990cdb38234e97fc66254cdbcd71)) ## [4.0.0](https://github.com/yargs/y18n/compare/v3.2.1...v4.0.0) (2017-10-10) ### Bug Fixes * allow support for falsy values like 0 in tagged literal ([#45](https://github.com/yargs/y18n/issues/45)) ([c926123](https://github.com/yargs/y18n/commit/c926123)) ### Features * **__:** added tagged template literal support ([#44](https://github.com/yargs/y18n/issues/44)) ([0598daf](https://github.com/yargs/y18n/commit/0598daf)) ### BREAKING CHANGES * **__:** dropping Node 0.10/Node 0.12 support y18n-5.0.8/LICENSE000066400000000000000000000013331403340010000133110ustar00rootroot00000000000000Copyright (c) 2015, Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. y18n-5.0.8/README.md000066400000000000000000000062171403340010000135710ustar00rootroot00000000000000# y18n [![NPM version][npm-image]][npm-url] [![js-standard-style][standard-image]][standard-url] [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) The bare-bones internationalization library used by yargs. Inspired by [i18n](https://www.npmjs.com/package/i18n). ## Examples _simple string translation:_ ```js const __ = require('y18n')().__; console.log(__('my awesome string %s', 'foo')); ``` output: `my awesome string foo` _using tagged template literals_ ```js const __ = require('y18n')().__; const str = 'foo'; console.log(__`my awesome string ${str}`); ``` output: `my awesome string foo` _pluralization support:_ ```js const __n = require('y18n')().__n; console.log(__n('one fish %s', '%d fishes %s', 2, 'foo')); ``` output: `2 fishes foo` ## Deno Example As of `v5` `y18n` supports [Deno](https://github.com/denoland/deno): ```typescript import y18n from "https://deno.land/x/y18n/deno.ts"; const __ = y18n({ locale: 'pirate', directory: './test/locales' }).__ console.info(__`Hi, ${'Ben'} ${'Coe'}!`) ``` You will need to run with `--allow-read` to load alternative locales. ## JSON Language Files The JSON language files should be stored in a `./locales` folder. File names correspond to locales, e.g., `en.json`, `pirate.json`. When strings are observed for the first time they will be added to the JSON file corresponding to the current locale. ## Methods ### require('y18n')(config) Create an instance of y18n with the config provided, options include: * `directory`: the locale directory, default `./locales`. * `updateFiles`: should newly observed strings be updated in file, default `true`. * `locale`: what locale should be used. * `fallbackToLanguage`: should fallback to a language-only file (e.g. `en.json`) be allowed if a file matching the locale does not exist (e.g. `en_US.json`), default `true`. ### y18n.\_\_(str, arg, arg, arg) Print a localized string, `%s` will be replaced with `arg`s. This function can also be used as a tag for a template literal. You can use it like this: __`hello ${'world'}`. This will be equivalent to `__('hello %s', 'world')`. ### y18n.\_\_n(singularString, pluralString, count, arg, arg, arg) Print a localized string with appropriate pluralization. If `%d` is provided in the string, the `count` will replace this placeholder. ### y18n.setLocale(str) Set the current locale being used. ### y18n.getLocale() What locale is currently being used? ### y18n.updateLocale(obj) Update the current locale with the key value pairs in `obj`. ## Supported Node.js Versions Libraries in this ecosystem make a best effort to track [Node.js' release schedule](https://nodejs.org/en/about/releases/). Here's [a post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a). ## License ISC [npm-url]: https://npmjs.org/package/y18n [npm-image]: https://img.shields.io/npm/v/y18n.svg [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg [standard-url]: https://github.com/feross/standard y18n-5.0.8/deno.ts000066400000000000000000000003621403340010000136020ustar00rootroot00000000000000import { y18n as _y18n } from './build/lib/index.js' import type { Y18NOpts } from './build/lib/index.d.ts' import shim from './lib/platform-shims/deno.ts' const y18n = (opts: Y18NOpts) => { return _y18n(opts, shim) } export default y18n y18n-5.0.8/index.mjs000066400000000000000000000002671403340010000141330ustar00rootroot00000000000000import shim from './build/lib/platform-shims/node.js' import { y18n as _y18n } from './build/lib/index.js' const y18n = (opts) => { return _y18n(opts, shim) } export default y18n y18n-5.0.8/lib/000077500000000000000000000000001403340010000130525ustar00rootroot00000000000000y18n-5.0.8/lib/cjs.ts000066400000000000000000000003171403340010000142020ustar00rootroot00000000000000import { y18n as _y18n, Y18NOpts } from './index.js' import nodePlatformShim from './platform-shims/node.js' const y18n = (opts: Y18NOpts) => { return _y18n(opts, nodePlatformShim) } export default y18n y18n-5.0.8/lib/index.ts000066400000000000000000000142331403340010000145340ustar00rootroot00000000000000export interface Y18NOpts { directory?: string; updateFiles?: boolean; locale?: string; fallbackToLanguage?: boolean; } interface Work { directory: string; locale: string; cb: Function } export interface Locale { [key: string]: string } interface CacheEntry { [key: string]: string; } export interface PlatformShim { fs: { readFileSync: Function, writeFile: Function }, exists: Function, format: Function, resolve: Function } let shim: PlatformShim class Y18N { directory: string; updateFiles: boolean; locale: string; fallbackToLanguage: boolean; writeQueue: Work[]; cache: {[key: string]: {[key: string]: CacheEntry|string}}; constructor (opts: Y18NOpts) { // 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: (string|Function)[]): string { if (typeof arguments[0] !== 'string') { return this._taggedLiteral(arguments[0] as string[], ...arguments) } const str: string = args.shift() as string let cb: Function = function () {} // start with noop. if (typeof args[args.length - 1] === 'function') cb = (args.pop() as Function) 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 as string[])) } __n () { const args = Array.prototype.slice.call(arguments) const singular: string = args.shift() const plural: string = args.shift() const quantity: number = 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] as CacheEntry 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: (string|number)[] = [str] if (~str.indexOf('%d')) values.push(quantity) return shim.format.apply(shim.format, values.concat(args)) } setLocale (locale: string) { this.locale = locale } getLocale () { return this.locale } updateLocale (obj: Locale) { 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: string[], ...args: string[]) { 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: 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: Error) { _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: string, locale: string) { 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: string) { return shim.exists(file) } } export function y18n (opts: Y18NOpts, _shim: PlatformShim) { 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 } } y18n-5.0.8/lib/platform-shims/000077500000000000000000000000001403340010000160175ustar00rootroot00000000000000y18n-5.0.8/lib/platform-shims/deno.ts000066400000000000000000000014321403340010000173140ustar00rootroot00000000000000/* global Deno */ import { posix } from 'https://deno.land/std/path/mod.ts' import { sprintf } from 'https://deno.land/std/fmt/printf.ts' export default { fs: { readFileSync: (path: string) => { try { return Deno.readTextFileSync(path) } catch (err) { // Fake the same error as Node.js, so that it does not bubble. err.code = 'ENOENT' throw err } }, writeFile: Deno.writeFile }, format: sprintf, resolve: (base: string, p1: string, p2: string) => { try { return posix.resolve(base, p1, p2) } catch (err) { // Most likely we simply don't have --allow-read set. } }, exists: (file: string) => { try { return Deno.statSync(file).isFile } catch (err) { return false } } } y18n-5.0.8/lib/platform-shims/node.ts000066400000000000000000000005101403340010000173100ustar00rootroot00000000000000import { readFileSync, statSync, writeFile } from 'fs' import { format } from 'util' import { resolve } from 'path' export default { fs: { readFileSync, writeFile }, format, resolve, exists: (file: string) => { try { return statSync(file).isFile() } catch (err) { return false } } } y18n-5.0.8/package.json000066400000000000000000000033571403340010000146020ustar00rootroot00000000000000{ "name": "y18n", "version": "5.0.8", "description": "the bare-bones internationalization library used by yargs", "exports": { ".": [ { "import": "./index.mjs", "require": "./build/index.cjs" }, "./build/index.cjs" ] }, "type": "module", "module": "./build/lib/index.js", "keywords": [ "i18n", "internationalization", "yargs" ], "homepage": "https://github.com/yargs/y18n", "bugs": { "url": "https://github.com/yargs/y18n/issues" }, "repository": "yargs/y18n", "license": "ISC", "author": "Ben Coe ", "main": "./build/index.cjs", "scripts": { "check": "standardx **/*.ts **/*.cjs **/*.mjs", "fix": "standardx --fix **/*.ts **/*.cjs **/*.mjs", "pretest": "rimraf build && tsc -p tsconfig.test.json && cross-env NODE_ENV=test npm run build:cjs", "test": "c8 --reporter=text --reporter=html mocha test/*.cjs", "test:esm": "c8 --reporter=text --reporter=html mocha test/esm/*.mjs", "posttest": "npm run check", "coverage": "c8 report --check-coverage", "precompile": "rimraf build", "compile": "tsc", "postcompile": "npm run build:cjs", "build:cjs": "rollup -c", "prepare": "npm run compile" }, "devDependencies": { "@types/node": "^14.6.4", "@wessberg/rollup-plugin-ts": "^1.3.1", "c8": "^7.3.0", "chai": "^4.0.1", "cross-env": "^7.0.2", "gts": "^3.0.0", "mocha": "^8.0.0", "rimraf": "^3.0.2", "rollup": "^2.26.10", "standardx": "^7.0.0", "ts-transform-default-export": "^1.0.2", "typescript": "^4.0.0" }, "files": [ "build", "index.mjs", "!*.d.ts" ], "engines": { "node": ">=10" }, "standardx": { "ignore": [ "build" ] } } y18n-5.0.8/renovate.json000066400000000000000000000001601403340010000150170ustar00rootroot00000000000000{ "extends": [ "config:base" ], "pinVersions": false, "rebaseStalePrs": true, "gitAuthor": null } y18n-5.0.8/rollup.config.js000066400000000000000000000007071403340010000154270ustar00rootroot00000000000000import ts from '@wessberg/rollup-plugin-ts' import transformDefaultExport from 'ts-transform-default-export' const output = { format: 'cjs', file: './build/index.cjs', exports: 'default' } if (process.env.NODE_ENV === 'test') output.sourcemap = true export default { input: './lib/cjs.ts', output, plugins: [ ts({ transformers: ({ program }) => ({ afterDeclarations: transformDefaultExport(program) }) }), ] } y18n-5.0.8/test/000077500000000000000000000000001403340010000132635ustar00rootroot00000000000000y18n-5.0.8/test/deno/000077500000000000000000000000001403340010000142105ustar00rootroot00000000000000y18n-5.0.8/test/deno/y18n-test.ts000066400000000000000000000005461403340010000163410ustar00rootroot00000000000000/* global Deno */ import { assertEquals } from 'https://deno.land/std/testing/asserts.ts' import y18n from '../../deno.ts' // Parser: Deno.test('smoke test', () => { const __ = y18n({ locale: 'pirate', directory: './test/locales' }).__ assertEquals( __`Hi, ${'Ben'} ${'Coe'}!`, 'Yarr! Shiver me timbers, why \'tis Ben Coe!' ) }) y18n-5.0.8/test/esm/000077500000000000000000000000001403340010000140475ustar00rootroot00000000000000y18n-5.0.8/test/esm/y18n-test.mjs000066400000000000000000000005721403340010000163420ustar00rootroot00000000000000/* global describe, it */ import * as assert from 'assert' import y18n from '../../index.mjs' describe('y18n', function () { it('__ smoke test', function () { const __ = y18n({ locale: 'pirate', directory: './test/locales' }).__ assert.strictEqual( __`Hi, ${'Ben'} ${'Coe'}!`, 'Yarr! Shiver me timbers, why \'tis Ben Coe!' ) }) }) y18n-5.0.8/test/locales/000077500000000000000000000000001403340010000147055ustar00rootroot00000000000000y18n-5.0.8/test/locales/bad-locale.json000066400000000000000000000000211403340010000175540ustar00rootroot00000000000000{"hello": "worl} y18n-5.0.8/test/locales/en.json000066400000000000000000000005031403340010000162000ustar00rootroot00000000000000{ "Hello": "Hello!", "Hello %s %s": "Hello %s %s", "%d cat": { "one": "%d cat", "other": "%d cats" }, "%d %s cat": { "one": "%d %s cat", "other": "%d %s cats" }, "There is one monkey in the %s": { "one": "There is one monkey in the %s", "other": "There are %d monkeys in the %s" } } y18n-5.0.8/test/locales/pirate.json000066400000000000000000000002601403340010000170620ustar00rootroot00000000000000{ "Hello": "Avast ye mateys!", "Hi, %s %s!": "Yarr! Shiver me timbers, why 'tis %s %s!", "%d cat": { "one": "%d land catfish", "other": "%d land catfishes" } } y18n-5.0.8/test/y18n-test.cjs000066400000000000000000000261511403340010000155450ustar00rootroot00000000000000/* global describe, it, after, beforeEach */ const expect = require('chai').expect const fs = require('fs') const rimraf = require('rimraf') const y18n = require('../build/index.cjs') const path = require('path') require('chai').should() describe('y18n', function () { describe('configure', function () { it('allows you to override the default y18n configuration', function () { const y = y18n({ locale: 'fr' }) y.locale.should.equal('fr') }) }) describe('_readLocaleFile', function () { it('throws a helpful error if language file has invalid syntax', function () { expect(function () { const __ = y18n({ locale: 'bad-locale', directory: path.join(__dirname, 'locales') }).__ __('Hello') }).to.throw(/syntax error/) }) }) describe('__', function () { it('can be used as a tag for template literals', function () { const __ = y18n({ locale: 'pirate', directory: path.join(__dirname, 'locales') }).__ __`Hi, ${'Ben'} ${'Coe'}!`.should.equal('Yarr! Shiver me timbers, why \'tis Ben Coe!') }) it('can be used as a tag for template literals with falsy arguments', function () { const __ = y18n({ locale: 'pirate', directory: path.join(__dirname, 'locales') }).__ __`Hi, ${'Ben'} ${''}!`.should.equal('Yarr! Shiver me timbers, why \'tis Ben !') }) it('uses replacements from the default locale if none is configured', function () { const __ = y18n({ directory: path.join(__dirname, 'locales') }).__ __('Hello').should.equal('Hello!') }) it('uses replacements from the configured locale', function () { const __ = y18n({ locale: 'pirate', directory: path.join(__dirname, 'locales') }).__ __('Hello').should.equal('Avast ye mateys!') }) it('uses language file if language_territory file does not exist', function () { const __ = y18n({ locale: 'pirate_JM', directory: path.join(__dirname, 'locales') }).__ __('Hello').should.equal('Avast ye mateys!') }) it('does not fallback to language file if fallbackToLanguage is false', function () { const __ = y18n({ locale: 'pirate_JM', fallbackToLanguage: false, updateFiles: false, directory: path.join(__dirname, 'locales') }).__ __('Hello').should.equal('Hello') }) it('uses strings as given if no matching locale files found', function () { const __ = y18n({ locale: 'zz_ZZ', updateFiles: false, directory: path.join(__dirname, 'locales') }).__ __('Hello').should.equal('Hello') }) it('expands arguments into %s placeholders', function () { const __ = y18n({ directory: path.join(__dirname, 'locales') }).__ __('Hello %s %s', 'Ben', 'Coe').should.equal('Hello Ben Coe') }) describe('the first time observing a word', function () { beforeEach(function (done) { rimraf('./test/locales/fr*.json', function () { return done() }) }) it('returns the word immediately', function () { const __ = y18n({ locale: 'fr', directory: path.join(__dirname, 'locales') }).__ __('banana').should.equal('banana') }) it('writes new word to locale file if updateFiles is true', function (done) { const __ = y18n({ locale: 'fr_FR', directory: path.join(__dirname, 'locales') }).__ __('banana', function (err) { const locale = JSON.parse(fs.readFileSync('./test/locales/fr_FR.json', 'utf-8')) locale.banana.should.equal('banana') return done(err) }) }) it('writes new word to language file if language_territory file does not exist', function (done) { fs.writeFileSync('./test/locales/fr.json', '{"meow": "le meow"}', 'utf-8') const __ = y18n({ locale: 'fr_FR', directory: path.join(__dirname, 'locales') }).__ __('meow').should.equal('le meow') __('banana', function (err) { const locale = JSON.parse(fs.readFileSync('./test/locales/fr.json', 'utf-8')) locale.banana.should.equal('banana') return done(err) }) }) it('writes word to missing locale file, if no fallback takes place', function (done) { fs.writeFileSync('./test/locales/fr.json', '{"meow": "le meow"}', 'utf-8') const __ = y18n({ locale: 'fr_FR', fallbackToLanguage: false, directory: path.join(__dirname, 'locales') }).__ __('banana', function (err) { // 'banana' should be written to fr_FR.json const locale = JSON.parse(fs.readFileSync('./test/locales/fr_FR.json', 'utf-8')) locale.should.deep.equal({ banana: 'banana' }) // fr.json should remain untouched const frJson = JSON.parse(fs.readFileSync('./test/locales/fr.json', 'utf-8')) frJson.should.deep.equal({ meow: 'le meow' }) return done(err) }) }) it('handles enqueuing multiple writes at the same time', function (done) { const __ = y18n({ locale: 'fr', directory: path.join(__dirname, 'locales') }).__ __('apple') __('banana', function () { __('foo') __('bar', function (err) { const locale = JSON.parse(fs.readFileSync('./test/locales/fr.json', 'utf-8')) locale.apple.should.equal('apple') locale.banana.should.equal('banana') locale.foo.should.equal('foo') locale.bar.should.equal('bar') return done(err) }) }) }) it('does not write the locale file if updateFiles is false', function (done) { const __ = y18n({ locale: 'fr', updateFiles: false, directory: path.join(__dirname, 'locales') }).__ __('banana', function (err) { fs.existsSync('./test/locales/fr.json').should.equal(false) return done(err) }) }) }) }) describe('__n', function () { it('uses the singular form if quantity is 1', function () { const __n = y18n({ directory: path.join(__dirname, 'locales') }).__n __n('%d cat', '%d cats', 1).should.equal('1 cat') }) it('uses the plural form if quantity is greater than 1', function () { const __n = y18n({ directory: path.join(__dirname, 'locales') }).__n __n('%d cat', '%d cats', 2).should.equal('2 cats') }) it('allows additional arguments to be printed', function () { const __n = y18n({ directory: path.join(__dirname, 'locales') }).__n __n('%d %s cat', '%d %s cats', 2, 'black').should.equal('2 black cats') }) it('allows an alternative locale to be set', function () { const __n = y18n({ locale: 'pirate', directory: path.join(__dirname, 'locales') }).__n __n('%d cat', '%d cats', 1).should.equal('1 land catfish') __n('%d cat', '%d cats', 3).should.equal('3 land catfishes') }) // See: https://github.com/bcoe/yargs/pull/210 it('allows a quantity placeholder to be provided in the plural but not singular form', function () { const __n = y18n({ directory: path.join(__dirname, 'locales') }).__n const singular = __n('There is one monkey in the %s', 'There are %d monkeys in the %s', 1, 'tree') const plural = __n('There is one monkey in the %s', 'There are %d monkeys in the %s', 3, 'tree') singular.should.equal('There is one monkey in the tree') plural.should.equal('There are 3 monkeys in the tree') }) describe('the first time observing a pluralization', function () { beforeEach(function (done) { rimraf('./test/locales/fr.json', function () { return done() }) }) it('returns the pluralization immediately', function () { const __n = y18n({ locale: 'fr', directory: path.join(__dirname, 'locales') }).__n __n('%d le cat', '%d le cats', 1).should.equal('1 le cat') }) it('writes to the locale file if updateFiles is true', function (done) { const __n = y18n({ locale: 'fr', directory: path.join(__dirname, 'locales') }).__n __n('%d apple %s', '%d apples %s', 2, 'dude', function (err) { const locale = JSON.parse(fs.readFileSync('./test/locales/fr.json', 'utf-8')) locale['%d apple %s'].one.should.equal('%d apple %s') locale['%d apple %s'].other.should.equal('%d apples %s') return done(err) }) }) it('does not write the locale file if updateFiles is false', function (done) { const __n = y18n({ locale: 'fr', updateFiles: false, directory: path.join(__dirname, 'locales') }).__n __n('%d apple %s', '%d apples %s', 2, 'dude', function (err) { fs.existsSync('./test/locales/fr.json').should.equal(false) return done(err) }) }) }) }) describe('setLocale', function () { it('switches the locale', function () { const i18n = y18n({ directory: path.join(__dirname, 'locales') }) i18n.__('Hello').should.equal('Hello!') i18n.setLocale('pirate') i18n.__('Hello').should.equal('Avast ye mateys!') }) }) describe('updateLocale', function () { beforeEach(function (done) { rimraf('./test/locales/fr.json', function () { return done() }) }) it('updates the locale with the new lookups provided', function () { const i18n = y18n({ locale: 'fr', directory: path.join(__dirname, 'locales') }) i18n.updateLocale({ foo: 'le bar' }) i18n.__('foo').should.equal('le bar') }) it('loads the locale from disk prior to updating the map', function () { fs.writeFileSync('./test/locales/fr.json', '{"meow": "le meow"}', 'utf-8') const i18n = y18n({ locale: 'fr', directory: path.join(__dirname, 'locales') }) i18n.updateLocale({ foo: 'le bar' }) i18n.__('meow').should.equal('le meow') }) }) describe('getLocale', function () { it('returns the configured locale', function () { y18n().getLocale().should.equal('en') }) }) // See: https://github.com/yargs/y18n/issues/96, // https://github.com/yargs/y18n/pull/107 describe('prototype pollution', () => { it('does not pollute prototype, with __proto__ locale', () => { const y = y18n() y.setLocale('__proto__') y.updateLocale({ polluted: '👽' }) y.__('polluted').should.equal('👽') ;(typeof polluted).should.equal('undefined') }) it('does not pollute prototype, when __ is used with __proto__ locale', () => { const __ = y18n({ locale: '__proto__' }).__ __('hello') ;(typeof {}.hello).should.equal('undefined') }) }) after(function () { rimraf.sync('./test/locales/fr.json') }) }) y18n-5.0.8/tsconfig.json000066400000000000000000000005051403340010000150130ustar00rootroot00000000000000{ "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { "outDir": "build", "rootDir": ".", "sourceMap": false, "target": "es2017", "moduleResolution": "node", "module": "es2015" }, "include": [ "lib/**/*.ts" ], "exclude": [ "lib/platform-shims/deno.ts" ] }y18n-5.0.8/tsconfig.test.json000066400000000000000000000002121403340010000157640ustar00rootroot00000000000000{ "extends": "./tsconfig.json", "compilerOptions": { "sourceMap": true }, "exclude": [ "lib/platform-shims/deno.ts" ] }