├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── index.js ├── license ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = Nanotranslate 3 | 4 | function Nanotranslate (dict) { 5 | // if (!dict.id) throw new Error('Nanotranslate: dict.id required') 6 | if (!dict.values) throw new Error('Nanotranslate: dict.values required') 7 | 8 | translate.id = dict.id 9 | function translate (key, data) { 10 | return Nanotranslate.run(dict, key, data) 11 | } 12 | 13 | return translate 14 | } 15 | 16 | Nanotranslate.run = function runTranslate (dict, key, data) { 17 | var value = dict.values[key] 18 | 19 | if (value === undefined) { 20 | throw new TypeError('translate: "' + key + '" not found in dictionary "' + dict.id + '".') 21 | } 22 | 23 | if (Array.isArray(value)) { 24 | var count = data != null ? data.count : undefined 25 | 26 | if (typeof count !== 'number') { 27 | throw new TypeError('translate: "' + key + '" is pluralized, second argument must be an object with field "count" as a number.') 28 | } 29 | 30 | value = value[Math.abs(count)] || value[value.length - 1] 31 | } 32 | 33 | for (var templateKey in data) { 34 | value = value.replace(new RegExp('{{\\s*' + templateKey + '\\s*}}', 'g'), data[templateKey]) 35 | } 36 | 37 | return value 38 | } 39 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Andrew Joslin (ajoslin.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nanotranslate", 3 | "main": "index.js", 4 | "version": "2.0.0", 5 | "description": "Translate with pluralization and variables in under 600b.", 6 | "license": "MIT", 7 | "repository": "ajoslin/nanotranslate", 8 | "author": { 9 | "name": "Andrew Joslin", 10 | "email": "andrewtjoslin@gmail.com", 11 | "url": "ajoslin.com" 12 | }, 13 | "scripts": { 14 | "test": "standard && tape test.js" 15 | }, 16 | "keywords": [ 17 | "translate", 18 | "i18n", 19 | "micro", 20 | "small", 21 | "nano", 22 | "payload" 23 | ], 24 | "devDependencies": { 25 | "tape": "^4.0.0", 26 | "standard": "^7.0.0" 27 | }, 28 | "files": [ 29 | "index.js", 30 | "test.js" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # nanotranslate [![Build Status](https://travis-ci.org/ajoslin/nanotranslate.svg?branch=master)](https://travis-ci.org/ajoslin/nanotranslate) 2 | 3 | > Translate with pluralization and variables in under 600b. 4 | 5 | ## Install 6 | 7 | ``` 8 | $ npm install --save nanotranslate 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | var Translate = require('nanotranslate') 15 | 16 | var translate = Translate({ 17 | id: 'en_US', 18 | values: { 19 | SIMPLE: 'simple string', 20 | TEMPLATED: 'simple {{value}} string', 21 | PLURAL: [ 22 | 'Zero items left.', 23 | 'One item left.', 24 | '{{count}} items left, {{name}}.' 25 | ], 26 | FN: ({ value }) => 'Hello, ' + value 27 | } 28 | }) 29 | 30 | translate.id // => 'en_US' 31 | 32 | translate('SIMPLE') // => 'simple string' 33 | translate('TEMPLATED', {value: 'hello'}) // => 'simple hello string' 34 | translate('PLURAL', {count: 1}) // => 'Zero items left.' 35 | translate('PLURAL', {count: 2}) // => 'One item left.' 36 | translate('PLURAL', {count: 3, name: 'Alex'}) // => '3 items left, Alex.' 37 | translate('FN', { value: 'world'}) // => 'Hello, world' 38 | ``` 39 | 40 | ## License 41 | 42 | MIT © [Andrew Joslin](http://ajoslin.com) 43 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tape') 4 | var Translate = require('./') 5 | 6 | var translate = Translate({ 7 | id: 'en_US', 8 | values: { 9 | PLAIN_STRING: 'hello', 10 | PLURALIZED_SIMPLE: [ 11 | 'Here are {{ count }}', 12 | 'Here is {{ count }}', 13 | 'And larger: {{ count }}' 14 | ], 15 | 16 | TEMPLATED: '{{foo}} {{ bar}} baz', 17 | 18 | APPLE: [ 19 | 'apples', 20 | 'apple', 21 | 'apples' 22 | ], 23 | COMPLEX: [ 24 | 'No {{item}} left.', 25 | 'One {{item}} left.', 26 | '{{count}} {{item}} left.' 27 | ] 28 | } 29 | }) 30 | 31 | test('basic', t => { 32 | t.equal(translate.id, 'en_US') 33 | t.equal(translate('PLAIN_STRING'), 'hello') 34 | t.end() 35 | }) 36 | 37 | test('not found', t => { 38 | t.throws(() => translate('NOT_FOUND'), /"NOT_FOUND" not found/) 39 | t.end() 40 | }) 41 | 42 | test('pluralized simple', t => { 43 | t.throws(() => translate('PLURALIZED_SIMPLE', {}), /pluralized/) 44 | t.throws(() => translate('PLURALIZED_SIMPLE', {count: 'not a number'}), /pluralized/) 45 | 46 | t.equal(translate('PLURALIZED_SIMPLE', {count: 0}), 'Here are 0') 47 | t.equal(translate('PLURALIZED_SIMPLE', {count: 1}), 'Here is 1') 48 | t.equal(translate('PLURALIZED_SIMPLE', {count: 2}), 'And larger: 2') 49 | t.equal(translate('PLURALIZED_SIMPLE', {count: 3}), 'And larger: 3') 50 | t.equal(translate('PLURALIZED_SIMPLE', {count: 999}), 'And larger: 999') 51 | t.equal(translate('PLURALIZED_SIMPLE', {count: 0}), 'Here are 0') 52 | t.equal(translate('PLURALIZED_SIMPLE', {count: -1}), 'Here is -1') 53 | t.equal(translate('PLURALIZED_SIMPLE', {count: -2}), 'And larger: -2') 54 | t.equal(translate('PLURALIZED_SIMPLE', {count: -3}), 'And larger: -3') 55 | t.end() 56 | }) 57 | 58 | test('templates', t => { 59 | t.equal(translate('TEMPLATED', {foo: 1, bar: 2}), '1 2 baz') 60 | t.equal(translate('TEMPLATED', {bar: 2}), '{{foo}} 2 baz') 61 | t.end() 62 | }) 63 | 64 | test('complex', t => { 65 | t.equal( 66 | translate('COMPLEX', { 67 | item: translate('APPLE', {count: 0}), 68 | count: 0 69 | }), 70 | 'No apples left.' 71 | ) 72 | 73 | t.equal( 74 | translate('COMPLEX', { 75 | item: translate('APPLE', {count: 1}), 76 | count: 1 77 | }), 78 | 'One apple left.' 79 | ) 80 | 81 | t.equal( 82 | translate('COMPLEX', { 83 | item: translate('APPLE', {count: 999}), 84 | count: 999 85 | }), 86 | '999 apples left.' 87 | ) 88 | t.end() 89 | }) 90 | 91 | test('separate Translate.run function', t => { 92 | t.equal( 93 | Translate.run({ 94 | values: { 95 | HELLO: 'hello {{world}}' 96 | } 97 | }, 'HELLO', {world: 'planet'}), 98 | 'hello planet' 99 | ) 100 | t.end() 101 | }) 102 | --------------------------------------------------------------------------------