@vocab/cli 0.0.16 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +184 -21
  3. package/package.json +3 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @vocab/cli
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`87333d7`](https://github.com/seek-oss/vocab/commit/87333d79c4a883b07d7d8f2c272b16e2243c49bd) [#80](https://github.com/seek-oss/vocab/pull/80) Thanks [@askoufis](https://github.com/askoufis)! - Enable the creation of generated languages via the `generatedLanguages` config.
8
+ See [the docs] for more information and examples.
9
+
10
+ [the docs]: https://github.com/seek-oss/vocab#generated-languages
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`87333d7`](https://github.com/seek-oss/vocab/commit/87333d79c4a883b07d7d8f2c272b16e2243c49bd)]:
15
+ - @vocab/core@1.1.0
16
+
17
+ ## 1.0.1
18
+
19
+ ### Patch Changes
20
+
21
+ - [`3ec6dba`](https://github.com/seek-oss/vocab/commit/3ec6dbaad590299cc33e2d9d4a877576eb05853a) [#63](https://github.com/seek-oss/vocab/pull/63) Thanks [@jahredhope](https://github.com/jahredhope)! - Migrate to new @formatjs/icu-messageformat-parser as intl-messageformat-parser has been deprecated
22
+
23
+ - Updated dependencies [[`3ec6dba`](https://github.com/seek-oss/vocab/commit/3ec6dbaad590299cc33e2d9d4a877576eb05853a)]:
24
+ - @vocab/core@1.0.2
25
+
26
+ ## 1.0.0
27
+
28
+ ### Major Changes
29
+
30
+ - [`3031054`](https://github.com/seek-oss/vocab/commit/303105440851db6126f0606e1607745b27dd981c) [#51](https://github.com/seek-oss/vocab/pull/51) Thanks [@jahredhope](https://github.com/jahredhope)! - Release v1.0.0
31
+
32
+ Release Vocab as v1.0.0 to signify a stable API and support future [semver versioning](https://semver.org/) releases.
33
+
34
+ Vocab has seen a lot of iteration and changes since it was first published on 20 November 2020. We are now confident with the API and believe Vocab is ready for common use.
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies [[`0074382`](https://github.com/seek-oss/vocab/commit/007438273ef70f5d5ded45777933651ad8df36f6), [`3031054`](https://github.com/seek-oss/vocab/commit/303105440851db6126f0606e1607745b27dd981c)]:
39
+ - @vocab/core@1.0.0
40
+ - @vocab/phrase@1.0.0
41
+
3
42
  ## 0.0.16
4
43
 
5
44
  ### Patch Changes
package/README.md CHANGED
@@ -2,34 +2,31 @@
2
2
 
3
3
  Vocab is a strongly typed internationalization framework for React.
4
4
 
5
- ## Getting started
5
+ Vocab helps you ship multiple languages without compromising the reliability of your site or slowing down delivery.
6
6
 
7
- ### Step 1: Install Dependencies
7
+ - Shareable translations
8
8
 
9
- Vocab is a monorepo with different packages you can install depending on your usage, the below list will get you started using the cli, React and webpack integrations.
9
+ Translations are co-located with the components that use them. Vocab uses the module graph allowing shared components to be installed with package managers like npm, just like any other module.
10
10
 
11
- ```bash
12
- $ npm i --save @vocab/cli @vocab/react @vocab/webpack
13
- ```
11
+ - Loading translations dynamically
14
12
 
15
- ### Step 2: Setup Webpack plugin
13
+ Vocab only loads the current user's language. If the language changes Vocab can load the new language behind the scenes without reloading the page.
16
14
 
17
- Before starting to write code you'll need to setup webpack to understand how to use `translation.json` files.
15
+ - Strongly typed with TypeScript
18
16
 
19
- This is done using the **VocabWebpackPlugin**.
17
+ When using translations TypeScript will ensure code only accesses valid translations and translations are passed all required dynamic values.
20
18
 
21
- **webpack.config.js**
19
+ ## Getting started
22
20
 
23
- ```js
24
- const VocabWebpackPlugin = require('@vocab/webpack').default;
21
+ ### Step 1: Install Dependencies
25
22
 
26
- module.exports = {
27
- ...,
28
- plugins: [new VocabWebpackPlugin({})]
29
- }
23
+ Vocab is a monorepo with different packages you can install depending on your usage, the below list will get you started using the cli, React and webpack integrations.
24
+
25
+ ```bash
26
+ $ npm i --save @vocab/cli @vocab/react @vocab/webpack
30
27
  ```
31
28
 
32
- ### Step 3: Configure Vocab
29
+ ### Step 2: Configure Vocab
33
30
 
34
31
  You can configure Vocab directly when calling the API or via a `vocab.config.js` file.
35
32
 
@@ -113,7 +110,24 @@ So far, your app will run, but you're missing any translations other than the in
113
110
  }
114
111
  ```
115
112
 
116
- ### Step 6: Optimize for fast page loading
113
+ ### Step 6: [Optional] Set up Webpack plugin
114
+
115
+ Right now every language is loaded into your web application all the time, which could lead to a large bundle size. Ideally you will want to switch out the Node/default runtime for web runtime that will load only the active language.
116
+
117
+ This is done using the **VocabWebpackPlugin**. Applying this plugin to your client webpack configuration will replace all vocab files with a dynamic asynchronous chunks designed for the web.
118
+
119
+ **webpack.config.js**
120
+
121
+ ```js
122
+ const { VocabWebpackPlugin } = require('@vocab/webpack');
123
+
124
+ module.exports = {
125
+ ...,
126
+ plugins: [new VocabWebpackPlugin()]
127
+ }
128
+ ```
129
+
130
+ ### Step 7: [Optional] Optimize for fast page loading
117
131
 
118
132
  Using the above method without optimizing what chunks webpack uses you may find the page needing to do an extra round trip to load languages on a page.
119
133
 
@@ -157,8 +171,10 @@ In the below example we use two messages, one that passes in a single parameter
157
171
  Vocab will automatically parse these strings as ICU messages, identify the required parameters and ensure TypeScript knows the values must be passed in.
158
172
 
159
173
  ```tsx
160
- t('my key with param', {name: 'Vocab'});
161
- t('my key with component', {Link: children => (<a href="/foo">{children}</Link>)});
174
+ t('my key with param', { name: 'Vocab' });
175
+ t('my key with component', {
176
+ Link: (children) => <a href="/foo">{children}</a>
177
+ });
162
178
  ```
163
179
 
164
180
  ## Configuration
@@ -168,6 +184,14 @@ Configuration can either be passed into the Node API directly or be gathered fro
168
184
  **vocab.config.js**
169
185
 
170
186
  ```js
187
+ function capitalize(element) {
188
+ return element.toUpperCase();
189
+ }
190
+
191
+ function pad(message) {
192
+ return '[' + message + ']';
193
+ }
194
+
171
195
  module.exports = {
172
196
  devLanguage: 'en',
173
197
  languages: [
@@ -176,11 +200,25 @@ module.exports = {
176
200
  { name: 'en-US', extends: 'en' },
177
201
  { name: 'fr-FR' }
178
202
  ],
203
+ /**
204
+ * An array of languages to generate based off translations for existing languages
205
+ * Default: []
206
+ */
207
+ generatedLanguages: [
208
+ {
209
+ name: 'generatedLangauge',
210
+ extends: 'en',
211
+ generator: {
212
+ transformElement: capitalize,
213
+ transformMessage: pad
214
+ }
215
+ }
216
+ ],
179
217
  /**
180
218
  * The root directory to compile and validate translations
181
219
  * Default: Current working directory
182
220
  */
183
- projectRoot: ['./example/'];
221
+ projectRoot: './example/',
184
222
  /**
185
223
  * A custom suffix to name vocab translation directories
186
224
  * Default: '.vocab'
@@ -193,6 +231,131 @@ module.exports = {
193
231
  };
194
232
  ```
195
233
 
234
+ ## Generated languages
235
+
236
+ Vocab supports the creation of generated languages via the `generatedLanguages` config.
237
+
238
+ Generated languages are created by running a message `generator` over every translation message in an existing translation.
239
+ A `generator` may contain a `transformElement` function, a `transformMessage` function, or both.
240
+ Both of these functions accept a single string parameter and return a string.
241
+
242
+ `transformElement` is applied to string literal values contained within `MessageFormatElement`s.
243
+ A `MessageFormatElement` is an object representing a node in the AST of a compiled translation message.
244
+ Simply put, any text that would end up being translated by a translator, i.e. anything that is not part of the [ICU Message syntax], will be passed to `transformElement`.
245
+ An example of a use case for this function would be adding [diacritics] to every letter in order to stress your UI from a vertical line-height perspective.
246
+
247
+ `transformMessage` receives the entire translation message _after_ `transformElement` has been applied to its individual elements.
248
+ An example of a use case for this function would be adding padding text to the start/end of your messages in order to easily identify which text in your app has not been extracted into a `translations.json` file.
249
+
250
+ By default, a generated language's messages will be based off the `devLanguage`'s messages, but this can be overridden by providing an `extends` value that references another language.
251
+
252
+ **vocab.config.js**
253
+
254
+ ```js
255
+ function capitalize(message) {
256
+ return message.toUpperCase();
257
+ }
258
+
259
+ function pad(message) {
260
+ return '[' + message + ']';
261
+ }
262
+
263
+ module.exports = {
264
+ devLanguage: 'en',
265
+ languages: [{ name: 'en' }, { name: 'fr' }],
266
+ generatedLanguages: [
267
+ {
268
+ name: 'generatedLanguage',
269
+ extends: 'en',
270
+ generator: {
271
+ transformElement: capitalize,
272
+ transformMessage: pad
273
+ }
274
+ }
275
+ ]
276
+ };
277
+ ```
278
+
279
+ Generated languages are consumed the same way as regular languages.
280
+ Any Vocab API that accepts a `language` parameter will work with a generated language as well as a regular language.
281
+
282
+ **App.tsx**
283
+
284
+ ```tsx
285
+ const App = () => (
286
+ <VocabProvider language="generatedLanguage">
287
+ ...
288
+ </VocabProvider>
289
+ );
290
+ ```
291
+
292
+ [icu message syntax]: https://formatjs.io/docs/intl-messageformat/#message-syntax
293
+ [diacritics]: https://en.wikipedia.org/wiki/Diacritic
294
+
295
+ ## Pseudo-localization
296
+
297
+ The `@vocab/pseudo-localize` package exports low-level functions that can be used for pseudo-localization of translation messages.
298
+
299
+ ```ts
300
+ import {
301
+ extendVowels,
302
+ padString,
303
+ pseudoLocalize,
304
+ substituteCharacters
305
+ } from '@vocab/pseudo-localize';
306
+
307
+ const message = 'Hello';
308
+
309
+ // [Hello]
310
+ const paddedMessage = padString(message);
311
+
312
+ // Ḩẽƚƚö
313
+ const substitutedMessage = substituteCharacters(message);
314
+
315
+ // Heelloo
316
+ const extendedMessage = extendVowels(message);
317
+
318
+ // Extend the message and then substitute characters
319
+ // Ḩẽẽƚƚöö
320
+ const pseudoLocalizedMessage = pseudoLocalize(message);
321
+ ```
322
+
323
+ Pseudo-localization is a transformation that can be applied to a translation message.
324
+ Vocab's implementation of this transformation contains the following elements:
325
+
326
+ - _Start and end markers (`padString`):_ All strings are encapsulated in `[` and `]`. If a developer doesn’t see these characters they know the string has been clipped by an inflexible UI element.
327
+ - _Transformation of ASCII characters to extended character equivalents (`substituteCharacters`):_ Stresses the UI from a vertical line-height perspective, tests font and encoding support, and weeds out strings that haven’t been externalized correctly (they will not have the pseudo-localization applied to them).
328
+ - _Padding text (`extendVowels`):_ Simulates translation-induced expansion. Vocab's implementation of this involves repeating vowels (and `y`) to simulate a 40% expansion in the message's length.
329
+
330
+ This Netflix technology [blog post][blog post] inspired Vocab's implementation of this
331
+ functionality.
332
+
333
+ ### Generating a pseudo-localized language using Vocab
334
+
335
+ Vocab can generate a pseudo-localized language via the [`generatedLanguages` config][generated languages config], either via the webpack plugin or your `vocab.config.js` file.
336
+ `@vocab/pseudo-localize` exports a `generator` that can be used directly in your config.
337
+
338
+ **vocab.config.js**
339
+
340
+ ```js
341
+ const { generator } = require('@vocab/pseudo-localize');
342
+
343
+ module.exports = {
344
+ devLanguage: 'en',
345
+ languages: [{ name: 'en' }, { name: 'fr' }],
346
+ generatedLanguages: [
347
+ {
348
+ name: 'pseudo',
349
+ extends: 'en',
350
+ generator
351
+ }
352
+ ]
353
+ };
354
+ ```
355
+
356
+ [blog post]: https://netflixtechblog.com/pseudo-localization-netflix-12fff76fbcbe
357
+ [generated languages config]: #generated-languages
358
+
196
359
  ## Use without React
197
360
 
198
361
  If you need to use Vocab outside of React, you can access the returned Vocab file directly. You'll then be responsible for when to load translations and how to update on translation load.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vocab/cli",
3
- "version": "0.0.16",
3
+ "version": "1.1.0",
4
4
  "main": "dist/vocab-cli.cjs.js",
5
5
  "module": "dist/vocab-cli.esm.js",
6
6
  "bin": {
@@ -10,12 +10,11 @@
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@types/env-ci": "^3.1.0",
13
- "@vocab/core": "^0.0.11",
14
- "@vocab/phrase": "^0.0.11",
13
+ "@vocab/core": "^1.1.0",
14
+ "@vocab/phrase": "^1.0.0",
15
15
  "env-ci": "^5.0.2",
16
16
  "fast-glob": "^3.2.4",
17
17
  "form-data": "^3.0.0",
18
- "intl-messageformat-parser": "^6.0.16",
19
18
  "node-fetch": "^2.6.1",
20
19
  "prettier": "^2.1.2",
21
20
  "yargs": "^16.1.0"