facebook / fbt Goto Github PK
View Code? Open in Web Editor NEWA JavaScript Internationalization Framework
Home Page: https://facebook.github.io/fbt
License: MIT License
A JavaScript Internationalization Framework
Home Page: https://facebook.github.io/fbt
License: MIT License
Manifest and maybe other cli should support .ts and .tsx files
I think the transform will just work as babel 7 supports typescript
The unit tests we have can only be run in our internal test infra.
We need to create a new Jest config to make it testable in a stand-alone environment (outside FB infra).
Ideally, it could then be run on Travis externally. A nice to have for later.
Currently the landing page at https://facebookincubator.github.io/fbt/ shows the english text text
with a descriptor eg
, an object with translation information keyed on that eg
, and then a resultant translated snippet. However, the translation in the translated snippet is nowhere to be found in the translation object, so even if it's just to show an except of how things work, it's a good idea to show that there is a relation between all three aspects: currently the translated snippet does not appear to be based on anything related to the other two bits of code shown.
There are currently 3 separate CLI scripts to use (manifest, collection, translation), all requiring different CLI arguments and coordination. We should simplify this per the following:
babel-plugin-fbt
takes should be readable from this config file.This is a Facebook-ism that we probably don't want to keep in the open source release. Perhaps it should have a hook to set a logger (eg. fbt.setLogger(....)
) rather than having a hard dependency on Banzai (which is mocked out in this open source release).
Hey folks!
I got 2 important questions to consider this in our next project:
.mo
files and vice versa ?Cheers
Currently the babel-plugin-fbt transform needs to see untransformed JSX <fbt>
nodes before any other JSX babel transforms to perform its transpilation. This is non-standard, and we should fix it. Essentially, enable the same JSX code, but post-JSX transform and accept something like
React.createElement("fbt", ...)
and still turn this into
fbt._(...)
at runtime
In effect, the transform should be able to work the same whether there's another transform layer in front or not.
I see the field translationGroups in translation_input.json includes many locales, but the file seems too large when you have a lot of translations. So, does fbt or babel-plugin-fbt support splitting translationGroups into different files?
We should publish transforms and runtime to npm to enable easier integration into existing web-apps. Within this process, we should fix the haste "global" paths in our runtime require statements and convert to the CommonJS relative file-paths (like our transforms).
it would be great to have a step by step tutorial on docs of how to setup fbt in an existing React app project
which packages should we install?
how should we modify our babel config?
should we modify our webpack config?
how to translate new generated hash keys?
I'm working in an ES2017 codebase compiled with TypeScript, Babel, and webpack. It seems that in certain contexts, using the default import of FBT fails because babel-plugin-fbt
says the variable fbt
is not bound when using the fbt()
syntax in code. Examining the value of the import, is an object with this structure:
{
__esModule: true,
FbtTranslations: { /* ... */ },
GenderConst: { /* ... */ },
default: f (),
fbt: f (),
init: f (e),
}
I'm not sure which step of tooling is responsible for making the default export an object.
I've confirmed that a reliable workaround is to write const { fbt } = require('fbt')
rather than const fbt = require('fbt')
.
I believe that the fault lies in babel-plugin-fbt
, since I am able to use the default exports of other libraries without issue.
Not certain what the minimal replication case is.
When using TypeScript and/or webpack, FBT should work reliably when using the default export.
System:
OS: macOS Sierra 10.12.6
CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Memory: 288.96 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 12.12.0 - ~/.nvm/versions/node/v12.12.0/bin/node
Yarn: 1.19.1 - /usr/local/bin/yarn
npm: 6.11.3 - ~/.nvm/versions/node/v12.12.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Hi there! The Docusaurus team is currently developing Docusaurus 2 and are almost done with an MVP that doesn't include translations and versioning features, which means there's feature parity with your website's current usage of Docusaurus.
Docusaurus 2 brings about many improvements:
I will be your POC in helping you throughout the migration process. Let me know what you think!
First of all, thanks for making fbt open source! 🙏
Currently the transform script only outputs a single file, which in the application gets passed to the fbt's init function. This means that we need to load all translations at once! Are there any plans on supporting generating multiple translation files that could then be dynamically loaded by fbt when needed? For starters it would for example be nice to split translations on locale.
For cross-platform do not use unix util find
You can use npm package shx
'yarn manifest' failed in windows
PS Z:\sources\react\fbt\demo-app> yarn manifest
yarn run v1.12.3
$ babel-node ../transform/babel-plugin-fbt/bin/manifest --src src
�� ������ 䠩�: '*$FbtEnum.js'
child_process.js:645
throw err;
^
Error: Command failed: find . -type f -iname '*$FbtEnum.js'
�� ������ 䠩�: '*$FbtEnum.js'
at checkExecSyncError (child_process.js:602:13)
at execSync (child_process.js:642:13)
at Object.<anonymous> (Z:\sources\react\fbt\transform\babel-plugin-fbt\bin\manifest.js:76:3)
at Module._compile (module.js:653:30)
at Module._compile (Z:\sources\react\fbt\node_modules\pirates\lib\index.js:83:24)
at Module._extensions..js (module.js:664:10)
at Object.newLoader (Z:\sources\react\fbt\node_modules\pirates\lib\index.js:88:7)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
PS Z:\sources\react\fbt\demo-app>
Sometimes, calling the translate script produces the following error:
Error: EAGAIN: resource temporarily unavailable, read
at Object.readSync (fs.js:498:3)
at tryReadSync (fs.js:332:20)
at Object.readFileSync (fs.js:369:19)
at Object.<anonymous> (/node_modules/babel-plugin-fbt/bin/translate.js:209:31)
Hi! Thanks for open sourcing this. Looks exciting!
Plural handling jumped out at me as very basic:
https://facebookincubator.github.io/fbt/docs/plurals
As is, it looks like a simplistic-yet-very-specific one/many handling with support for showing a counter.
Since fbt is used at Facebook (a product with a pretty involved translation) I'm curious if that is actually enough for Facebook itself?
For context, several languages (Russian for example) have many types of plurals. The ICU message syntax is my go-to for supporting these cases:
http://userguide.icu-project.org/formatparse/messages (it is used by FormatJS so should be familiar to users of formatjs projects).
I have interesting bug, I use locales like('en', 'ru')
_getNumberModuleForLang
always return default value IntlCLDRNumberType01
Because:
locale = 'ru';
getNumberVariations()
=> IntlNumberType.get(locale)
=> FBLocaleToLang.get(locale) // return ''
----------------------------------^^^ here bug
Snippet:
(()=>{
const _locToLang = {
"cx_PH": "ceb",
"ck_US": "chr",
"fb_AA": "en",
"fb_AC": "en",
"fbt_AC": "en",
"fb_HA": "en",
"fb_AR": "ar",
"fb_HX": "en",
"fb_LS": "en",
"fb_LL": "en",
"fb_RL": "en",
"fb_ZH": "zh",
"tl_PH": "fil",
"sy_SY": "syr",
"qc_GT": "quc",
"tl_ST": "tlh",
"gx_GR": "grc",
"qz_MM": "my",
"eh_IN": "hi",
"cb_IQ": "ckb",
"zz_TR": "zza",
"tz_MA": "tzm",
"sz_PL": "szl",
"bp_IN": "bho",
"ns_ZA": "nso",
"fv_NG": "fuv",
"em_ZM": "bem",
"qr_GR": "rup",
"qk_DZ": "kab",
"qv_IT": "vec",
"qs_DE": "dsb",
"qb_DE": "hsb",
"qe_US": "esu",
"bv_DE": "bar",
"qt_US": "tli",
"nh_MX": "nah",
"tq_AR": "tob",
"fn_IT": "fur",
"lr_IT": "lij"
};
const FBLocaleToLang = {
get: function(locale) {
return _locToLang[locale] || locale.substring(0, locale.indexOf('_'));
},
};
return FBLocaleToLang.get('en'); // return ""
return FBLocaleToLang.get('ru'); // return ""
})();
Hi, quick question. I didn't see on docs if fbt is mean to be used on a react server side rendered (SSR). It is? :)
Thanks for all the work!
The following blows up because the enum isn't required via require(...)
, but instead uses import
import { fbt } from 'fbt-runtime'
import CategoriesEnum from './Categories$FbtEnum'
function category(listing) {
return (
<span>
<fbt desc="Category">
<fbt:enum enum-range={CategoriesEnum} value={listing.category} />
</fbt>
</span>
)
}
export default category
When I run next command:
node ../node_modules/babel-plugin-fbt/bin/collectFBT.js --pretty --json-input < i18n/src-manifest.json
I get an error:
src/features/usersPage/Page/index.js:
TypeError: Property value expected type of string but got null
/home/i/all-work/synergeto/admin-ui/node_modules/babel-plugin-fbt/bin/collectFBT.js:131
throw new Error(`Failed in ${errCount} files`);
^
Error: Failed in 4 files
at writeOutput (/home/i/all-work/synergeto/admin-ui/node_modules/babel-plugin-fbt/bin/collectFBT.js:131:11)
at ReadStream.<anonymous> (/home/i/all-work/synergeto/admin-ui/node_modules/babel-plugin-fbt/bin/collectFBT.js:169:5)
at ReadStream.emit (events.js:203:15)
at endReadableNT (_stream_readable.js:1129:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
A babel config to fix this problem:
const fbtEnumManifest = require('./.i18n/enum-manifest.json');
const presets = ['@exeto/babel-preset-react'];
const plugins = [
['babel-plugin-fbt', { fbtEnumManifest }],
'babel-plugin-fbt-runtime',
];
// To fix problem with Fbt
+if (process.env.NODE_ENV === 'development') {
+ plugins.push('react-hot-loader/babel');
+}
module.exports = {
presets,
plugins,
};
Hey
I've tried to run the live-demo-app together with (@jrwats youtube-instructions), and followed the instructions in the README
.
yarn manifest
yarn collect-fbts
yarn translate-fbts
yarn build
yarn start
when I try to switch between langauges nothing changes (beside align-right
of RTL languages)
How do I activate the translations ?
Sorry if this has been brought up somewhere before.
Would you consider using babel-plugin-macros
for babel-plugin-fbt
? It looks like a good fit for an imperative tool and would allow adoption in create-react-app
projects. You could probably rework the demo a bit to use it and be a little more concise.
When you forgot add a many
prop:
<fbt:plural {/*many="seals"*/} showCount="ifMany" count={Number(count)}>
a seal
</fbt:plural>
You will see an error like this:
src/Plurals/index.js:
TypeError: Cannot read property 'start' of undefined
/home/i/open-source/fbtDemo/node_modules/babel-plugin-fbt/bin/collectFBT.js:156
throw new Error(`Failed in ${errCount} files`);
^
Error: Failed in 1 files
at writeOutput (/home/i/open-source/fbtDemo/node_modules/babel-plugin-fbt/bin/collectFBT.js:156:11)
at ReadStream.<anonymous> (/home/i/open-source/fbtDemo/node_modules/babel-plugin-fbt/bin/collectFBT.js:194:5)
at ReadStream.emit (events.js:203:15)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
I don't know how can I fix the next problem:
import * as React from 'react';
// @ts-ignore : 'fbt' is declared but its value is never read.
import fbt from 'fbt';
console.log(fbt); // ( 1 )
// @ts-ignore
const node = <fbt desc='desc-text'>Some text</fbt>;
fbt is not bound. Did you forget to require('fbt')
Did It mean that webpack bundle remove unused import before babel transpilation?
childParentMappings:{}
And node_modules/babel-plugin-fbt/bin/collectFBT.js
couldn't collect any phrases from code
dependencies": {
"fbt": "^0.9.51",
"babel-plugin-fbt": "^0.9.12",
"babel-plugin-fbt-runtime": "^0.9.1",
}
I'm trying to understand the fbt
demo app a little better. I think I understand what most scripts are doing, but I don't understand where any of the ./translations/*.json
files come from. They seem to be auto generated due to there being base64 strings as keys.
Are these json files auto generated? Or compiled by hand?
(to be clear - I'm not asking about the actual translated text, I'm asking about how these files get generated for me to fill in with my own translations for phrases 😊)
In the npm package for fbt, 0.9.5, in node_modules, fbt/lib/intlList.js
has this for line 25:
var React = require("./React");
However, there's no React in the lib
folder so with webpack it fails to resolve require('fbt/lib/intlList')
. In the source file though, https://github.com/facebookincubator/fbt/blob/master/runtime/shared/intlList.js, there's an absolute import for React, const React = require('React')
.
Is this an error? And if so can we update the version of the npm package?
For next example
<fbt desc="" doNotExtract>Test</fbt>
I get this error:
TypeError: Cannot read property 'type' of null
at node_modules/babel-plugin-fbt/FbtUtil.js:140:15
at Array.forEach (<anonymous>)
at getOptionsFromAttributes (node_modules/babel-plugin-fbt/FbtUtil.js:130:18)
at PluginPass.JSXElement (node_modules/babel-plugin-fbt/index.js:153:13)
at newFn (node_modules/@babel/traverse/lib/visitors.js:193:21)
at NodePath._call (node_modules/@babel/traverse/lib/path/context.js:53:20)
at NodePath.call (node_modules/@babel/traverse/lib/path/context.js:40:17)
at NodePath.visit (node_modules/@babel/traverse/lib/path/context.js:88:12)
at TraversalContext.visitQueue (node_modules/@babel/traverse/lib/context.js:118:16)
at TraversalContext.visitSingle (node_modules/@babel/traverse/lib/context.js:90:19)
// this example work fine
<fbt desc="" doNotExtract={true}>Test</fbt>
{
"babel-plugin-fbt": "^0.9.14",
}
Please provide an event emitter as part of FBT, so that developers can gain insights into how FBT operates under the hood.
FBT currently offers very little in the way of reporting what work goes on behind the scenes. For instance, how can a developer know if a provided string is being translated at runtime? Making fbt
an event emitter with hooks for parts of the string substition process (language lookup, string lookup, etc.) would make the library easily introspectable. It would allow teams to collect statistics and determine where translations are missing, among other potential features.
This example would show how Sentry could be triggered when a translation is missing.
const fbt = require('fbt');
const Sentry = require('@sentry/browser');
fbt.on('missingTranslation', (language, hash) => {
Sentry.withScope(scope => {
scope.setExtras({ hash, language });
Sentry.captureMessage('Missing translation');
});
});
Potential users would be incentivized to choose FBT over competing solutions with better real-time data reporting. Executives want assurance that chosen software solutions are reliable. Without being able to inspect FBT as it works in real-time, developers would have to spend time writing more code and tooling to ensure that their translation system works as expected.
babel-plugin-fbt
allows developers to use an attribute common
to FBT calls which use JSX syntax. However, no documentation exists on https://facebookincubator.github.io/fbt/ for this feature.
It is not clear how or why to use the common
string API.
This feature would allow organizations or developers to define a set of frequently occurring strings which are reusable in FBT
The common string API could be a productivity boon to teams which have strings with specific meanings that need to be conveyed in a centralized location.
Hey, thanks for the nice package,
I have a question, does fbt support html string string?
like in react-intl theres formatHTMLmessage method that accept html element like:
formatHTMLmessage ('id','<span>test html string</span>')
does fbt support this?
I try to run getting started page instructions with latest version of yarn
package (1.15.2
) and getting error babel-node: command not found
. Command works property with some previous versions of yarn
(1.10.0
). Also everything works property if I using npm run
command. Maybe it's better to replace scripts commands with npm run
?
Link to gettings started instructions:
https://facebookincubator.github.io/fbt/docs/getting_started
Our NPM publishing is hairy and easy to get wrong. That, and it'd be nice to get CHANGELOG updates for free via lerna-changelog.
Hi! I think we need to keep track of both translated/untranslated items to ensure the app is properly translated.
I believe you guys doing automate this copy & paste(source_strings.json
to trasnlation_input.json
) process and maybe already planned to handle this issue while implementing that already, but I'm opening this issue for just clarification.
Currently the landing page at https://facebookincubator.github.io/fbt/ uses <a href="#">
, which is semantic markup for "navigate to the top of the page". It is not a placeholder link, and it's definitely worth changing that to something like <button><fbt desc="eg">text</fbt></button>
instead.
<fbt>
strips any newlines and spaces that sit in between constructs that don't neighbor raw text. This is at odds with what is typically produced by any other native DOM markup. This was originally to maintain parity with PHP/XHP/Hack's , but that decision is now in question. Especially in light of open-sourcing this to the greater JS community, we should try and support an API that is intuitive lacks surprises.
<fbt desc="d">
<span>
Where do
</span>
<b>spaces</b>
<i>go?</i>
Good
<i>question</i>
!
</fbt>
produces the following runtime
fbt._("{=Where do}{=spaces}{=go?} Good {=question} !",[...])
Compare this with what would have been produced in a browser:
Where do spaces go? Good question !
I have a project where I use decorators, when I run collect operation it fails as it doesn't have proper babel plugins configured for my source code.
How to solve the next problem:
// @flow
import React, { useState, useCallback } from 'react';
import fbt, { IntlViewerContext } from 'fbt';
const Links = () => {
return <fbt desc="test">Text link</fbt>;
};
const App = () => {
const setState = useState({})[1];
const onClick = useCallback(() => {
IntlViewerContext.locale = Math.random() > 0.5 ? 'en_GB' : 'de_DE';
setState({});
}, [setState]);
return (
<div onClick={onClick}>
<fbt desc="test">Title</fbt>
<Links />
{/*
rerender a `<Links/>` component never would call
and text does not change when set new locale
*/}
</div>
);
};
Can you remove tests\mock files?
/translate/__tests__
/bin/__tests__
/__mocks__
/__tests__
/__tests__
I was running a codemod to convert <div prop={"value"} />
to <div prop="value" />
, and I ran into a bug because the transform doesn't handle a string literal value property, only a string within a jsx expression container.
To be specific, the transform errors out if you change
<fbt:enum
enum-range={EnumName}
value={'Value'}
/>
to
<fbt:enum
enum-range={EnumName}
value="Value"
/>
Pronouns can be consolidated into the following values (as opposed to the 13 we have currently).
pronoun subject possessive reflextive object
NOT_A_PERSON "they" "their "themself" "this"
UNKNOWN_SINGULAR "they" "their" "themself" "them"
UNKNOWN_PLURAL "they" "their" "themselves" "them"
MALE "he" "his" "himself" "him"
FEMALE "she" "her" "herself" "her"
On footer all the links are using the docUrl
method that is adding the language on the url:
However, these links are not correct. Getting an 404 error.
Although maybe in the future will be support for many languages, I think is better to change this invalid links for the valid links right now.
node ../node_modules/babel-plugin-fbt/bin/collectFBT.js
internal/modules/cjs/loader.js:638
throw err;
^
Error: Cannot find module 'fbjs/lib/invariant'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15)
at Function.Module._load (internal/modules/cjs/loader.js:562:25)
at Module.require (internal/modules/cjs/loader.js:690:17)
at require (internal/modules/cjs/helpers.js:25:18)
at Object.<anonymous> (/home/i/all-work/synergeto/node_modules/babel-plugin-fbt/fbt-shift-enums.js:19:19)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
babel-plugin-fbt
currently contains the following dictionary of Facebook-specific strings in the library, used for the common
API: https://github.com/facebookincubator/fbt/blob/cf5743af96e48ce49cda9f3258c5d1102684f1b7/packages/babel-plugin-fbt/FbtCommonConstants.js
Developers should be able to register their own set of common strings, instead.
This proposal relates to #108. For the common string API to be useful, developers need to be able to say which strings are constant.
A developer could provide a JSON file which contains a dictionary of common strings. For instance, this might be part of the collectFBT
command:
node ./node_modules/babel-plugin-fbt/bin/collectFBT --commonSrc ./config/i18n/commonStrings.json --manifest < .fbt/src_manifest.json > .fbt/source_strings.json
This feature would be useful for the same reason #108 would be useful: it would empower teams to do more with FBT.
When I run yarn install on windows, I get:
error An unexpected error occurred: "ENOTDIR: not a directory, scandir 'D:\fbt\demo-app\node_modules'".
I think it has to do with there being an existing file/link (not sure what that is) in the directory. If anyone can guide me to it's purpose, would be happy to send in a PR that resolves this
FBT currently uses a mock called FBTLogger
to log impressions of translations: https://github.com/facebookincubator/fbt/blob/3a5441708ca6b71c2c18fe5a952d1058a22306d1/runtime/shared/fbt.js#L28
This logging system should be configurable by developers, similar to (or possibly in the same system as) #109.
Similar to #109, developers and other stakeholders may want to know which translations are being used in a given web app. This could be used to trigger engineer notifications for when a translation is missing, or to log an occurrence for a special event.
Here's an example of how a logging API might work:
const fbt = require('fbt');
const consoleLogger = {
logImpression(hash) {
console.log('i18n hash lookup:', hash);
},
};
fbt.setLogger(consoleLogger);
Observability is an important facet of any mature web application. Without the ability to respond to changes in the internals of FBT, the library may not be attractive to potential stakeholders and less useful to developers.
Allow existing fbtEnumManifest
babel option to be specified as a path, so that babel config can be used in package.json
This is fully evident even in the example app:
<fbt desc="...">
<fbt:param name="name" gender={this.state.ex1Gender}>
<b className="padRight">{this.state.ex1Name}</b>
</fbt:param>
has shared
<a className="neatoLink" href="#">
<fbt:plural
many="photos"
showCount="ifMany"
count={this.state.ex1Count}>
a photo
</fbt:plural>
</a>
with you
</fbt>
Extracts to
{
"hashToText": {
"PqPPir8Kg9xSlqdednPFOg==": "{name} has shared {=a photo} with you"
},
"jsfbt": {
"t": {
"*": "{name} has shared {=a photo} with you"
},
"m": [
{
"token": "name",
"type": 1
}
]
}
},
{
"hashToText": {
"/gj3gwqx1z8Xw233oZgOpQ==": "{number} photos",
"8UZCD6gFUKN+U5UUo1I3/w==": " a photo"
},
"desc": "In the phrase: \"{=} has shared {=a photo} with you\"",
"jsfbt": {
"t": {
"*": "{number} photos",
"_1": " a photo"
},
"m": [
{
"token": "number",
"type": 2,
"singular": true
}
]
}
}
Notice both the "{=}"
token and the {=a photo}
token in the description:
'In the phrase: "{=} has shared {= a photo} with you"'
So two things wrong here. {=}
should be {name}
AND the {number} photos
variant of the <fbt:plural>
instance needs its own description such that its token corresponds to the inner text:
'In the phrase: "{name} has shared {=[number] photos} with you"'
There should be two separate outer strings here (not one):
"{name} has shared {=a photo} with you"
"{name} has shared {=[number] photos} with you"
and inner-strings should have separate descriptions:
'In the phrase: "{name} has shared {=a photo} with you"'
'In the phrase: "{name} has shared {=[number] photos} with you"'
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.