ttag-org / ttag Goto Github PK
View Code? Open in Web Editor NEW:orange_book: simple approach for javascript localization
Home Page: https://ttag.js.org/
License: MIT License
:orange_book: simple approach for javascript localization
Home Page: https://ttag.js.org/
License: MIT License
Since we have support for jt
it would be nice to document it in our docs.
The problem:
Fuzzy translations are applied on resolve:
#: uaprom/cs/domain/cms/Product/ProductList/views/GroupDiscountPopup/utils.js:12
#: uaprom/cs/domain/cms/Product/components/DiscountPopup/utils.js:11
#, fuzzy
msgid "${ counter } день"
msgid_plural "${ counter } дня"
msgstr[0] "${ count } клієнту"
msgstr[1] "${ count } клієнту"
msgstr[2] "${ count } клієнту"
leads to this transformation:
function getDaysCounterText(counter) {
return _tag_ngettext(counter, [count + ' \u043A\u043B\u0456\u0454\u043D\u0442\u0443', count + ' \u043A\u043B\u0456\u0454\u043D\u0442\u0443', count + ' \u043A\u043B\u0456\u0454\u043D\u0442\u0443']);
}
Solution:
Filter fuzzy translations on translations resolve
For now we are validating c-3po config with ajv
and it has validation schema. This works well but configuration errors are ugly and unreadable.
The task is to make them more clear (like webpack has for instance).
Example:
ERROR in ./app.js
Module build failed: Error: /home/a.mostovenko/git/webpack-demo/app.js: data.resolve should have required property 'translations'
at ConfigValidationError.Error (native)
at new ConfigValidationError (/home/a.mostovenko/git/webpack-demo/node_modules/babel-plugin-c-3po/dist/errors.js:21:131)
at new Config (/home/a.mostovenko/git/webpack-demo/node_modules/babel-plugin-c-3po/dist/config.js:152:19)
at PluginPass.extractOrResolve (/home/a.mostovenko/git/webpack-demo/node_modules/babel-plugin-c-3po/dist/plugin.js:21:22)
at newFn (/home/a.mostovenko/git/webpack-demo/node_modules/babel-traverse/lib/visitors.js:276:21)
at NodePath._call (/home/a.mostovenko/git/webpack-demo/node_modules/babel-traverse/lib/path/context.js:76:18)
at NodePath.call (/home/a.mostovenko/git/webpack-demo/node_modules/babel-traverse/lib/path/context.js:48:17)
Should be something like this:
Configuration error. config.data.resolve should have required property 'translations'.
As it was mentioned here, some new features that we want to add to c-3po will require extra dependencies. I am worried about keeping the core functionality as generic as possible.
In case that is described in the link above there is a feature request for react.js
library which adds react
and babel-preset-react
to the project dependencies.
So maybe it is reasonable to have an ability to extend c-3po library and c-3po plugin in a way that all extra dependencies will be installed only if you need them with a separate extension package.
There will be a separate package c-3po-react
. So you need to install it separately if you need rt
tag functionality.
c-3po-react
package will expose functions (tags) for wrapping translations. (rt
tag for instance).
This must work without transpile step.
import { rt } from 'c-3po-react'
rt`Click ${<a href="">here</a>}`
Aditionaly we must upgrade babel-plugin-c-3po
to be extensible via configuration. This can be done in something like this way:
.babelrc
plugins: [['c-3po', { extensions: ['c-3po-react'] }]]
After this config babel plugin should be able to extract, validate and resolve translations inside rt
tag.
In general, extensions would be easy to implement because c-3po
already uses all extractor functions as extensions. You can look at how t
tag is implemented - tag-gettext src
Current rough implementation for rt
tag is here - #21
Want to hear your feedback guys @Alxpy @MrOrz ? What do you think?
We need a separate migration guide doc.
As you asked for suggestions here is one 😅
It would be really cool to have an app template / integration into create-react-app.
Thanks to all contributors for this great piece of software!
This - https://c-3po.js.org/multiline-strings.html works only while using babel-pugin. Need to implement the same behaviour with pure c-3po lib.
One of the main feature request from other people, who sees c-3po
is the context
feature.
Context allows us to translate the same word (the same msgid) differently, depending on the context.
For C, C++ there are separate functions for applying context to the translation: pgettext, npgettext (for single and plural forms accordingly)
pgettext(<context>, <msgid>);
pngettext(<context>, <single form>, <plural form>, <count>);
As for me I don't like the idea of having separate functions for that. As it was designed for C initially, they almost had no other option. But we can make it better, so I would like to implement succinct API for the c-3po
.
To introduce special c
function, that will return an object with all c-3po functions with applied context. It will accept context value as a first argument.
import { c, t } from 'c-3po'
c('context').t`Hello ${ user.name }`
// maybe better to place it on a separate lines
c('context')
.t`Hello ${ user.name}`
import { ngettext, msgid, c } from 'c-3po'
c('context').ngettext(msgid`${ n } banana`, `${ n } bananas`, n);
One thing that I am worried about, is that syntax becomes a little bit overloaded. But in general, I think context is not so frequent case. And with this apporach, it is clear how to introduce some other concepts like domains
by just chaining them one after another.
d('domain')
.c('context')
.t`translate me`
Place context information right into the t
function call;
import { t } from 'c-3po'
t('context')`Hello ${ user.name}`
import { ngettext, msgid } from 'c-3po'
ngettext('context')(msgid`${ n } banana`, `${ n } bananas`, n);
This one looks a little bit cleaner that Proposal 1, but the question is how do we extend this to have domain info? Maybe something like this will work:
t({context: 'context', domain: 'domain'})`Hello ${ user.name}`
Implement separate configuration option that will specify multiple locales for translations resolve:
Example:
{
resolve: { translations: ['en_US.po', 'en.po'] }
}
Extracted comments (see --add-comments
part of the gettext doc, or developer comments) can be helpful for developers to hint the translator about the translated string. It would be nice if we support such feature.
/* this is foo */
const foo = t`pen pineapple apple pen`;
const bar = /* hello world */ngettext(/* this is bar */ msgid`${n} time clicked`, `${n} times clicked`, n);
Could extracts to something like:
#. this is foo
#: some-file
msgid "pen pineapple apple pen"
msgstr ""
#. hello world
#. this is bar
#: some-file
msgid "${ 0 } time clicked"
msgid_plural "${ 0 } times clicked"
msgstr[0] ""
msgstr[1] ""
As we will merge this PR's:
We should update doc properly:
Original issue - #65
babel plugin should resolve translations according to the context.
Source file:
import { t, c } from 'c-3po';
console.log(t`test`);
console.log(c('email').t`test`);
Example .po file:
msgid "test"
msgstr "test translate default"
msgctxt "email"
msgid "test"
msgstr "test translate email"
Result file:
import { t, c } from 'c-3po';
console.log('test translate default');
console.log('test translate email');
We need changelog.
The key idea for the enhancement is to have efficient dev and prod setup with c-3po.
Bellow I will try to explain in details on the webpack example:
po-gettext-loader
require.ensure
or async import). Can be also bundled in js assets, we don't care about the bundle size in dev.This setup will provide us fast builds (only one is needed), and fast feedback loop. Bigger bundles size and extra HTTP requests from the browser can be ignored in dev env).
c-3po
mock instead of the whole libSmaller assets, faster load, less CPU work, fewer HTTP queries.
The good news is that both setups can be implemented with existing versions of lib and plugin. You can check https://github.com/c-3po-org/webpack-demo/tree/translations-chunk-load (translations-chunk-load branch).
resolve
or extract
mode. Somehow we should be able to specify that we need only validation without transformations in the config.resolveDefault
if no extract
or resolve
config is specified. This part is not explicit and can be confusing. I think we should also change this default behavior.resolveDefault
transformations).resolveDefault
is executed when { resolve : { default : true }}
is present.According to this proposal #65 we should implement contexts feature.
Example:
import { c, t } from 'c-3po'
c('context').t`Hello ${ user.name }`
// maybe better to place it on a separate lines
c('context')
.t`Hello ${ user.name}`
Need to implement some arguments check for the ngettext
func.
msgid
.Hey there,
I'm the author of es2015-i18n-tag. I just stumbled across your implementation of a translation library based on ES6 template tags.
Just wanted to say that I'm happy this idea gets some more attention.
Especially your progress on pluralization looks interesting. How about joining forces to build a more feature rich implementation of an i18n template tag?
By the way, I like the name of your lib. It sounds much better than es2015-i18n-tag :)
Best regards
Steffen
Need to have some kind of doc generation for c-3po functions. (esdoc or something similar)
for now c-3po functions are discovered only by imports, but it is also necessary to discover them by require to make everything work on the backend side.
babel-plugin-c-3po should be able to discover this:
const { t } = require('c-3po');
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl
I think it makes sense to implement integration with built-in internationalization API. This can be handy while formatting dates and numbers (Here is a nice example https://github.com/skolmer/es2015-i18n-tag#standard-format-strings).
Spec referrence - http://www.ecma-international.org/ecma-402/2.0/
Example:
t`Hello ${ username}
For now this will be extracted to
msgid "Hello ${ 0 }"
This behaves so because we are missing variable names information while resolving translations at the runtime. (details described here).
But what if we will transform our translations object (json with translations) in form of Hello ${ username }
to Hello ${ 0 }
in the runtime. This operation will be not so hard to perform (traverse tree and clone). And with this solution we will have the best of 2 worlds:
Motivation - https://github.com/rollup/rollup/wiki/pkg.module.
can not resolve translation when n is 0 (runtime backend):
const m = 0;
console.log(ngettext(msgid`${m} minute`, `${m} minutes`, m));
We have an experimental nt
function for working with plural forms - https://c-3po.js.org/tag-ngettext--nt-.html.
So, the question is do we really need to support multiple functions to handle plurals? As for me, I'm ok with ngettext
. I suggest to remove nt
tag.
What do you think? @Alxpy @MrOrz
Seems like comments reference entry is not extracted properly and becomes too large when sortByMsgid
is present:
c-3po - 0.4.5
babel-plugin-c-3po - 0.4.0
uaprom/cs/domain/purchases/Forms/SubscriptionForm/components/Info.js:14/: RegExp too big
at RegExp.exec (native)
at RegExp.[Symbol.match] (native)
at String.match (native)
at buildPotData (/home/a.gurin/workspace/zakupki/node_modules/babel-plugin-c-3po/dist/po-helpers.js:30:62)
at PluginPass.post (/home/a.gurin/workspace/zakupki/node_modules/babel-plugin-c-3po/dist/plugin.js:32:3)
at File.call (/home/a.gurin/workspace/zakupki/node_modules/babel-core/lib/transformation/file/index.js:1:1)
at File.transform (/home/a.gurin/workspace/zakupki/node_modules/babel-core/lib/transformation/file/index.js:1:1)
at /home/a.gurin/workspace/zakupki/node_modules/babel-core/lib/transformation/pipeline.js:24:12
at File.wrap (/home/a.gurin/workspace/zakupki/node_modules/babel-core/lib/transformation/file/index.js:1:1)
at Pipeline.transform (/home/a.gurin/workspace/zakupki/node_modules/babel-core/lib/transformation/pipeline.js:22:12)
at transpile (/home/a.gurin/workspace/zakupki/node_modules/babel-loader/lib/index.js:22:12)
at Object.module.exports (/home/a.gurin/workspace/zakupki/node_modules/babel-loader/lib/index.js:1:1)
I think our library should support typescript as well. So it would be great to start with a type definitions for the public API.
https://facebook.github.io/jest/
It would be nice to research the pros and cons.
Inspired by this article - https://slack.engineering/localizing-slack-680c4bc7f45a
What is pseudolocalization? - https://en.wikipedia.org/wiki/Pseudolocalization
This feature will help us to identify untranslated strings more easily in dev mode.
case:
Project have modular structure. And each module need to self translator.
With global addLocale, useLocale
functions it looks difficult.
Maybe, it should be object (like Jed).
Or as domains-gettext functionally (issue) , but i need a object with gettext methods that ensure me that i choose the appropriate domain.
There must be the separate doc for c-3po lib API.
According to this proposal #65 we should implement contexts feature.
Example:
import { c, t } from 'c-3po'
c('context').t`Hello ${ user.name }`
Should be extracted to this:
msgstr: "Hello ${ user.name }"
msgctx: "context"
msgstr: ""
Doesn't matches the translation at a runtime (without babel plugin):
legal_entity_required: t`Вы воспользовались ЭЦП выданной физическому лицу.
Для подтверждения данных организации, код ЕГРПОУ должен содержать
не более 8 символов. Воспользуйтесь ЭЦП повторно.`,
translation in .po
#: uaprom/cs/domain/purchases/ECPVerification/i18n.js
msgid ""
"Вы воспользовались ЭЦП выданной физическому лицу. \n"
"Для подтверждения данных организации, код ЕГРПОУ должен содержать \n"
"не более 8 символов. Воспользуйтесь ЭЦП повторно."
msgstr ""
"Ви скористалися ЕЦП, що належить фізичній особі. "
"Для підтвердження даних організації, в коді ЄДРПОУ має міститися не більше 8 символів. Скористайтеся ЕЦП повторно."
Hey,
We successfully run this tool in a project, works well. However, one thing that may be slightly inconvenient is if you version the template.pot file, the order of the strings are different from build to build even if no strings are changed. It may pose a problem in that it generates merge conflicts, could it be an idea to sort the msgids alphabetically to mitigate this?
Provide mechanism to resolve translations on a backend (without babel).
referencing to this thread - #3 (comment)
think about how we can handle something like:
Нажмите <a onClick={this.handleClick}>здесь</a>.
maybe this can be an option
<translate>
Нажмите <a onClick={this.handleClick}>здесь</a>.
</translate>
extracted result
msgid "Нажмите <to>здесь<tc>"
msgstr ""
Example 2
Осталось: <span>{this.props.complainTextSymbolsLeft}</span>
{ungettext([' символ', ' символа', ' символов'], this.props.complainTextSymbolsLeft)}.
to - tag open
tc - tag close
There is defaultHeaders setup for babel-plugin. We need this in the library also.
Hello,
Are you planning to extend your API to provide gettext functionality (such as domains and contexts)?
I'm considering this lib to use in work (work with AST, validation and React solution is great). But i have several issues/questions
I hope the cooperation.
Extend api for loadLocale.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.