Code Monkey home page Code Monkey logo

mady's Introduction

Mady npm version

Mady v4 is a total rewrite of the venerable translation tool, dropping a lot of ballast, enabling new use cases and providing a huge performance boost. If you need features that have been removed (notably React Intl support), you can find v3's docs here. Otherwise, moving to v4 should be very straightforward -- see more details in the migration guide.

An easy-to-use tool to manage and translate ICU MessageFormat messages.

Mady UI

Although not depicted above, MessageFormat messages are supported. Please refer to this guide for more details on the syntax, but here is an example:

console.log(
  _t('someContext_{NUM, plural, one{1 hamburger} other{# hamburgers}}', {
    NUM: 1,
  })
);
// 1 hamburguesa
console.log(
  _t('someContext_{NUM, plural, one{1 hamburger} other{# hamburgers}}', {
    NUM: 2,
  })
);
// 2 hamburguesas

MessageFormat is not only for translation! Even if you only use English, you may need MessageFormat for gender, pluralisation and even regionalisation (regionalization).

Why?

  • Easy-to-use tool for parsing source files, editing translations, comparing languages side-by-side, filtering missing/dubious/unused translations, and compiling to (optionally minified) JavaScript modules.
  • A translation function to run the compiled language modules.
  • Multi-user support: multiple users can work simultaneously, with changes pushed in real time to all of them.
  • Automatic translations: Mady suggests automatic translations as soon as new messages or languages are added to its database.
  • Ultra-fast parsing: Mady watches your application folders and parses files as they are added, changed or deleted, then pushes all changes to the connected users.
  • MessageFormat messages: while it does not solve all the problems in the huge field of i18n, MessageFormat is a much more powerful tool than the conventional gettext (IMHO).
  • Full UNICODE support: messages and translations can include any UNICODE character. In other words, you can now translate 👍 (en) as 👏 (es-ES) and then 💃 (es-ES-andalusia)!
  • BCP47 support and locale inheritance: fetch missing translations from parent/child languages or dialects, and even sibling languages (other regions) as a last resort.
  • Dubious translations: flag some translations to revisit them later on.
  • Modular architecture, allowing advanced users to embed Mady as a translation tool in broader-scope tools (CMS, anyone?).
  • TypeScript support.

Installation

$ npm install --save mady
$ npm install --save-dev mady-server

Usage

There are two main parts in Mady: the web-based translation app and the translation function. The webapp is only needed during development (for the extraction, translation and management of messages and translations), whereas the translation function can be used in your own application, and hence in production as well.

The translation app

Access the translation app by running npx mady (run npx mady --help for more options, including changing the source paths). Mady will automatically launch in your default browser (default URL: http://localhost:8080). From the web application, you can:

  • Update the message database with new messages extracted from your source files
  • Translate your messages to the different supported languages
  • Mark translations as dubious
  • Apply filters: untranslated or unused messages, dubious translations, etc.
  • [Automatically] export translations to JS files with optional minification, for use by the translation function and other integrations

Messages in your source files might have the form: _t('someContext_Once upon a time...') (single or double quotes are supported), where _t() is the default name for the translation function (see below), someContext is some hint for the translator and Once upon a time... is your untranslated MessageFormat message.

Mady's configuration specifies key aspects such as the translation languages. Here is the default configuration (you'll find it under /locales/config.json):

{
  "srcPaths": ["src"],
  "srcExtensions": [".js", ".jsx", ".ts", ".tsx"],
  "langs": ["en"],
  "originalLang": "en",
  "msgFunctionNames": ["_t"],
  "msgRegexps": [],
  "fMinify": false,
  "fJsOutput": true
}

The translation function

Using the translation function is similarly straightforward:

import _t from 'mady';
import locales from './locales/es';

_t.setLocales(locales);

console.log(_t('someContext_Once upon a time...'));
// Érase una vez...
console.log(_t('someContext_Number of items: {NUM}', { NUM: 5 }));
// Número de ítems: 5
console.log(
  _t('someContext_{NUM, plural, one{1 hamburger} other{# hamburgers} }', {
    NUM: 1,
  })
);
// 1 hamburguesa
console.log(
  _t('someContext_{NUM, plural, one{1 hamburger} other{# hamburgers} }', {
    NUM: 2,
  })
);
// 2 hamburguesas

Alternatively, you can set up locales beforehand and then activate one or the other. In case the requested variant is not available, its closest BCP47 parent is enabled:

import _t from 'mady';
import locales from './locales/es';

_t.addLocales('es', locales);
const lang = _t.setLocales('es-US');
// returns 'es', since we don't have the American Spanish variant

const lang = _t.setLocales('fr');
// does nothing, since we have no French translations

BCP47 and translation fallbacks

Mady "fills in the gaps" when building translation modules ({lang.js}). In other words, when you don't provide a translation for message X for a particular language (e.g. es-ES), it will look for suitable fallback translations in related languages:

  • Parent languages (e.g. es)
  • Sibling languages (e.g. es-MX)
  • Children languages (e.g. es-ES-andalusia)

If you use Mady's translation function and it cannot find a suitable translation, it will just take the message key (e.g. someContext_Untranslated message), remove the context prefix (someContext_) and use it as a last-resort fallback.

The locales folder

You can find the following files in the locales folder (./locales by default):

  • A configuration file: config.json
  • A message file: keys.json
  • A raw translation file per language, e.g. fr.json (this is the one you edit with the web application)
  • A compiled translation module (fr.js), generated automatically.

MessageFormat

Mady uses the messageformat.js library by Alex Sexton, which "supports and extends all parts of the ICU MessageFormat standard (see the user guide), with the exception of the deprecated ChoiceFormat." IMHO, and while it does not solve all the problems in the huge field of i18n, it is a much more powerful tool than the conventional gettext.

Some examples of MessageFormat messages are given above (more here), but this does not even scratch the surface of what is enabled by this standard.

Why Mady?

This library is named after my cousin, a brilliant person with an extraordinary talent for languages. I thank her for teaching me English when I was a little child.

License (MIT)

Copyright (c) Guillermo Grau Panea 2016-2020

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

mady's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mady's Issues

Mady translations showing up blank

Hi @guigrpa , I've been trying to use mady in order to translate text for my project, formatted this way:

export const ENGLISH_STATE = {
  lang: 'en',
  messages: {
    'label': 'English',
    'login.password': 'Password',
    'login.confirm': 'Confirm Password',
  },
};

Because my project needs to follow this format, I thought of using the Regex option in order to add the text I want and see its translation:

screen shot 2018-10-09 at 12 46 41 pm

And, while that works, the translated text appears blank:

screen shot 2018-10-09 at 12 51 07 pm

I've also tried formatting my text so it looks like a function:

screen shot 2018-10-09 at 2 23 18 pm

But the same issue persists:

screen shot 2018-10-09 at 2 24 18 pm

Why would this be happening? Please help me understand if I'm not using it right. Should you need it, here is my config.json file:

{
  "dbVersion": 2,
  "srcPaths": [
    "src/testtranslation"
  ],
  "srcExtensions": [
    ".js",
    ".jsx",
    ".coffee",
    ".cjsx"
  ],
  "langs": [
    "en",
    "es",
    "ca",
    "en-US",
    "en-GB"
  ],
  "msgFunctionNames": [
    "_t"
  ],
  "msgRegexps": [
    "(?<=: ')(.*)(?=')"
  ],
  "fJsOutput": true,
  "fMinify": false,
  "fJsonOutput": true,
  "fReactIntlOutput": true,
  "originalLang": "en"
}

Feature requests: SSR support, description support

Kudos for building this. There's a lot to like, especially the seamless extract, auto-translate, edit, import cycle and the lovely UI. I think with a few enhancements it could be great!

In summary, I think you should consider teaming up with this project (https://github.com/format-message/format-message) as it has superior cli tooling for offline translation, and the mark up supports explicit IDs and description strings (quite a bit of file format work would be needed for the collaboration).

The biggest blocker for my use is the lack of a self-contained state object; something like "_t.namespace()". Or alternatively adding an option to pass the targeted locale to each invocation of _t like "_t(msg, params, locale)". The global functions like _t.setLocales don't work server side where the locale varies by the request context.

Update: actually, I can use mady pretty efficiently for SSR as is, as long as I define a wrapper function like so

function lclTranslate (message, args, locale) {
  _t.setLocales(locale)
  _t(message, args)
}

Error extracting React Intl messages

I'm really not sure what is the problem here, but it seems that the message extraction is not handling jsx and some other babel extensions correctly:

2017-05-20T16:14:00.554Z               parser ERROR Error extracting React Intl messages
                                       parser ERROR   name: 'SyntaxError'
                                       parser ERROR   message: 'unknown: Unexpected token (11:4)'
                                       parser ERROR   stack: SyntaxError: unknown: Unexpected token (11:4)
                                       parser ERROR   stack:    9 |
                                       parser ERROR   stack:   10 |   return (
                                       parser ERROR   stack: > 11 |     <Base>
                                       parser ERROR   stack:      |     ^
                                       parser ERROR   stack:   12 |       <Timer />
                                       parser ERROR   stack:   13 |     </Base>
                                       parser ERROR   stack:   14 |   );
2017-05-20T16:14:00.659Z               parser ERROR Error extracting React Intl messages
                                       parser ERROR   name: 'SyntaxError'
                                       parser ERROR   message: 'unknown: Unexpected token (46:12)'
                                       parser ERROR   stack: SyntaxError: unknown: Unexpected token (46:12)
                                       parser ERROR   stack:   44 |
                                       parser ERROR   stack:   45 |           const nextErr = {
                                       parser ERROR   stack: > 46 |             ...err,
                                       parser ERROR   stack:      |             ^
                                       parser ERROR   stack:   47 |             json
                                       parser ERROR   stack:   48 |           };
                                       parser ERROR   stack:   49 |           cb(nextErr);

Is there anything additional I need to take care of (besides having a babelrc ready)?

At the same time I was wondering if there is the possibility to use the messages that webpack/babel already extracted during the build. this way it would be easy to allow people to run mady on the extracted messages instead of giving them the whole code.

ES6 app translation error

I'm new to mady, I'd like to translate a React app using react-intl

Unfortunately, when starting the npm run translate script, the message list shows nothing:

image

And if I click on the refrech icon, I get the following error numerous times:

parser ERROR   message: unknown: Unexpected token (33:4)
                                       parser ERROR   message: 
                                       parser ERROR   message:   31 | }) => {
                                       parser ERROR   message:   32 |   const app = sheet.collectStyles((
                                       parser ERROR   message: > 33 |     <Provider store={store}>
                                       parser ERROR   message:      |     ^
                                       parser ERROR   message:   34 |       <StaticRouter basename='' context={context} location={url}>
                                       parser ERROR   message:   35 |         <App base_url={base_url} hostname={hostname} />
                                       parser ERROR   message:   36 |       </StaticRouter>

I think it might come from the ES6 syntax of my app. If so, how can I configure mady to handle it ?

Feature request

HI! I want to use mady alongside nunjucks for static html translation/generation. For that I need 2 things:

  • ability to save keys to a static JSON file
  • search for keys in nunjucks template format {{ keyname }}

How difficult do you think this could be?

MessageFormat translations resulting in warning messages

When using MessageFormat translations such as:
console.log(_t("someContext_{NUM, plural, one{1 hamburger} other{# hamburgers}}", { NUM: 2 }));
the following warning is generated:
Expected '===' and instead saw '=='
at the following return line:

var es = function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one' : 'other';
};

Perhaps the messageformat dependency needs to be updated?
Context: using the latest version of create-react-app

Possible bug: how are multiple translations for same key handled?

I'm confused by this feature. I toggled the FUZZY (yield sign) icon in the UI and I ended up with this in my es.json file. Notice, the same 'keyId' appears twice. Code inspection seems to indicate "last one wins", but since this is a JSON object, not an array, I think the sort order is dubious. And actually, when I look in my 'es.out.json' the FUZZY takes priority over the hand edited one.

 "7560a2b6-2d85-4021-a0e3-0fd04e927dd6": {
    "id": "7560a2b6-2d85-4021-a0e3-0fd04e927dd6",
    "isDeleted": false,
    "lang": "es",
    "translation": "los lagartos están calientes",
    "fuzzy": false,
    "keyId": "bGl6YXJkcyBhcmUgaG90"
  },
  "943d7b7b-9ad4-4567-8a55-032525504f47": {
    "id": "943d7b7b-9ad4-4567-8a55-032525504f47",
    "isDeleted": false,
    "lang": "es",
    "translation": "los lagartos están calientes",
    "fuzzy": true,
    "keyId": "bGl6YXJkcyBhcmUgaG90"
  }

I'm not sure what's supposed to happen here. Once I understand the intent I'd like to try to make a fix. Thanks.

(Actually, looking closer I don't think this is supposed to happen. I'm guessing it is a race condition).

update messages keys in UI by scanning minified version of react app

@guigrpa This is not an issue , but I don't know where to ask this question. So my requirement is to run mady UI in production environment and give the url to the translator so he can update the translations. However, in production I have minified version of my project. So npm run translate is not able to find messages from minified chunk.js files. Is there a way to achieve this.

Badly formatted translations crash the server process

How to reproduce:

  1. Click any translation field in the UI.
  2. Write {, or just any MessageFormat with oddly matched braces.
  3. Press Tab.

An exception is thrown. Since the exception is thrown inside an asynchronous callback without a try/catch (_.debounce 💣), it reaches the node.js main loop. Boom. The entire server process crashes. And no traceback whatsoever of who called the errored function.

/tmp/guigrpa-mady-9485b5e/src/server/db.js:395
        throw _iteratorError2;
        ^

Error: Parser error with `exampleContext_{NUM, plural, one{1 hamburger} other{# hamburgers} }`: SyntaxError: Expected "=", "}" or identifier but end of input found.
    at compileMsg (/tmp/guigrpa-mady-9485b5e/node_modules/messageformat/lib/messageformat.js:483:17)
    at MessageFormat.compile (/tmp/guigrpa-mady-9485b5e/node_modules/messageformat/lib/messageformat.js:508:21)
    at compileTranslations (compileTranslations.js:33:24)
    at _compileTranslations (db.js:286:25)
    at invokeFunc (/tmp/guigrpa-mady-9485b5e/node_modules/lodash/lodash.js:9402:23)
    at trailingEdge (/tmp/guigrpa-mady-9485b5e/node_modules/lodash/lodash.js:9450:18)
    at Timeout.timerExpired (/tmp/guigrpa-mady-9485b5e/node_modules/lodash/lodash.js:9437:18)
    at tryOnTimeout (timers.js:224:11)
    at Timer.listOnTimeout (timers.js:198:5)

npm ERR! Linux 4.5.1-1-ARCH
npm ERR! argv "/usr/bin/node" "/usr/bin/npm" "start"
npm ERR! node v5.10.1
npm ERR! npm  v3.8.7
npm ERR! code ELIFECYCLE
npm ERR! [email protected] start: `babel-node src/server/startup`
npm ERR! Exit status 1

It's kind of funny that less than 12 hours after I extensively wrote about this problem with node.js server programming in general I find it crashing applications on the wild.

You may want to do all your asynchronous staff with promises in order to be able to catch errors, since those have a built-in try catch that avoid them reaching the main loop and a mechanism to gracefully react to them (promise rejection). You may also want to have long stack traces in order to track what is causing the errors.

Error extracting React Intl messages

@guigrpa So I am new to mady and exploring possibilities it provides. In my react project I am using react-intl. I tried extracing existing messages from my project and ran npm run translate . when refresing the messages tab in mady UI. I got this error in console. Please explain to me why this error is coming and how to fix it.

 watch INFO  CHANGE on app/translations/keys.json
2018-09-06T07:04:42.409Z               parser INFO  Processing app/translations/keys.json...
2018-09-06T07:04:42.732Z               parser ERROR Error extracting React Intl messages
                                       parser ERROR   name: 'SyntaxError'
                                       parser ERROR   message: 'unknown: Unexpected token, expected ; (2:28)'
                                       parser ERROR   stack: SyntaxError: unknown: Unexpected token, expected ; (2:28)
                                       parser ERROR   stack:   1 | {
                                       parser ERROR   stack: > 2 |   "RGltZW5zaW9uIE5hbWUuLi4=": {
                                       parser ERROR   stack:     |                             ^
                                       parser ERROR   stack:   3 |     "id": "RGltZW5zaW9uIE5hbWUuLi4=",

Feature request maybe? Codesplitting by component/module/input-file.

Is it possible to split the translations by component?
I want each component to have it's own separate set of translation files.
So there would be many files for each locale, like:
Component1_en-US.json
Component1_de-DE.json
Component2_en-US.json
Component2_de-DE.json

I don't want to load the entire language for the app at once.

Working with React-Intl

Reading the readme, it semes that mady only works when message strings are wrapped in mady's own _t function. So, on first look it doesn't seem possible to extract react-intl's <FormattedMessage ... jsx syntax, or intl.formatMessage(). Is support for react-intl in the roadmap?

An alternative can be that I extract all my strings from my app into one JSON file, say en.json in this format:

[
  {
    "id": "PasswordForm.passwordField.label",
    "defaultMessage": "Choose your Password",
    "description": "Field lable for the password field"
  },
  ...
]

but then mady should be able to read this file and treat this as the keys database.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.