@vocab/react 1.1.3 → 1.1.5
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.
- package/LICENSE +21 -0
- package/README.md +66 -5
- package/dist/declarations/src/index.d.ts +25 -25
- package/dist/vocab-react.cjs.dev.js +2 -14
- package/dist/vocab-react.cjs.prod.js +2 -14
- package/dist/vocab-react.esm.js +0 -12
- package/package.json +6 -3
- package/CHANGELOG.md +0 -176
- package/src/index.tsx +0 -167
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
### MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 SEEK
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -422,20 +422,81 @@ Or to re-run the compiler when files change use:
|
|
|
422
422
|
$ vocab compile --watch
|
|
423
423
|
```
|
|
424
424
|
|
|
425
|
-
## External
|
|
425
|
+
## External Translation Tooling
|
|
426
426
|
|
|
427
427
|
Vocab can be used to synchronize your translations with translations from a remote translation platform.
|
|
428
428
|
|
|
429
|
-
| Platform
|
|
430
|
-
|
|
|
431
|
-
| [Phrase]
|
|
429
|
+
| Platform | Environment Variables |
|
|
430
|
+
| -------- | ----------------------------------- |
|
|
431
|
+
| [Phrase] | PHRASE_PROJECT_ID, PHRASE_API_TOKEN |
|
|
432
432
|
|
|
433
433
|
```bash
|
|
434
434
|
$ vocab push --branch my-branch
|
|
435
|
-
$ vocab push --branch my-branch --delete-unused-keys
|
|
436
435
|
$ vocab pull --branch my-branch
|
|
437
436
|
```
|
|
438
437
|
|
|
438
|
+
### [Phrase] Platform Features
|
|
439
|
+
|
|
440
|
+
#### Delete Unused keys
|
|
441
|
+
|
|
442
|
+
When uploading translations, Phrase identifies keys that exist in the Phrase project, but were not
|
|
443
|
+
referenced in the upload. These keys can be deleted from Phrase by providing the
|
|
444
|
+
`---delete-unused-keys` flag to `vocab push`. E.g.
|
|
445
|
+
|
|
446
|
+
```sh
|
|
447
|
+
$ vocab push --branch my-branch --delete-unused-keys
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
[phrase]: https://developers.phrase.com/api/
|
|
451
|
+
|
|
452
|
+
#### [Tags]
|
|
453
|
+
|
|
454
|
+
`vocab push` supports uploading [tags] to Phrase.
|
|
455
|
+
|
|
456
|
+
Tags can be added to an individual key via the `tags` property:
|
|
457
|
+
|
|
458
|
+
```jsonc
|
|
459
|
+
// translations.json
|
|
460
|
+
{
|
|
461
|
+
"Hello": {
|
|
462
|
+
"message": "Hello",
|
|
463
|
+
"tags": ["greeting", "home_page"]
|
|
464
|
+
},
|
|
465
|
+
"Goodbye": {
|
|
466
|
+
"message": "Goodbye",
|
|
467
|
+
"tags": ["home_page"]
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Tags can also be added under a top-level `_meta` field. This will result in the tags applying to all
|
|
473
|
+
keys specified in the file:
|
|
474
|
+
|
|
475
|
+
```jsonc
|
|
476
|
+
// translations.json
|
|
477
|
+
{
|
|
478
|
+
"_meta": {
|
|
479
|
+
"tags": ["home_page"]
|
|
480
|
+
},
|
|
481
|
+
"Hello": {
|
|
482
|
+
"message": "Hello",
|
|
483
|
+
"tags": ["greeting"]
|
|
484
|
+
},
|
|
485
|
+
"Goodbye": {
|
|
486
|
+
"message": "Goodbye"
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
In the above example, both the `Hello` and `Goodbye` keys would have the `home_page` tag attached to
|
|
492
|
+
them, but only the `Hello` key would have the `usage_greeting` tag attached to it.
|
|
493
|
+
|
|
494
|
+
**NOTE**: Only tags specified on keys in your [`devLanguage`][configuration] will be uploaded.
|
|
495
|
+
Tags on keys in other languages will be ignored.
|
|
496
|
+
|
|
497
|
+
[tags]: https://support.phrase.com/hc/en-us/articles/5822598372252-Tags-Strings-
|
|
498
|
+
[configuration]: #Configuration
|
|
499
|
+
|
|
439
500
|
## Troubleshooting
|
|
440
501
|
|
|
441
502
|
### Problem: Passed locale is being ignored or using en-US instead
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { TranslationFile, LanguageName, ParsedFormatFnByKey, ParsedFormatFn } from '@vocab/
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
|
|
4
|
-
interface TranslationsValue {
|
|
5
|
-
language: LanguageName;
|
|
6
|
-
locale?: Locale;
|
|
7
|
-
}
|
|
8
|
-
interface VocabProviderProps extends TranslationsValue {
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
}
|
|
11
|
-
export declare const VocabProvider: ({ children, language, locale, }: VocabProviderProps) => JSX.Element;
|
|
12
|
-
export declare const useLanguage: () => TranslationsValue;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
[key in keyof Params]: Params[key] extends ParsedFormatFn ? FormatXMLElementReactNodeFn : Params[key];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
<TranslationKey extends keyof FormatFnByKey>(key: TranslationKey, params: MapToReactNodeFunction<Parameters<FormatFnByKey[TranslationKey]>[0]>): ReturnType<FormatFnByKey[TranslationKey]> extends string ? string : ReactNode | string | Array<ReactNode | string>;
|
|
19
|
-
<TranslationKey extends keyof FormatFnByKey>(key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<string, any> ? never : TranslationKey): string;
|
|
20
|
-
};
|
|
21
|
-
export declare function useTranslations<Language extends string, FormatFnByKey extends ParsedFormatFnByKey>(translations: TranslationFile<Language, FormatFnByKey>): {
|
|
22
|
-
ready: boolean;
|
|
23
|
-
t: TranslateFn<FormatFnByKey>;
|
|
24
|
-
};
|
|
25
|
-
export {};
|
|
1
|
+
import type { TranslationFile, LanguageName, ParsedFormatFnByKey, ParsedFormatFn } from '@vocab/core';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
type Locale = string;
|
|
4
|
+
interface TranslationsValue {
|
|
5
|
+
language: LanguageName;
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
}
|
|
8
|
+
interface VocabProviderProps extends TranslationsValue {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare const VocabProvider: ({ children, language, locale, }: VocabProviderProps) => JSX.Element;
|
|
12
|
+
export declare const useLanguage: () => TranslationsValue;
|
|
13
|
+
type FormatXMLElementReactNodeFn = (parts: ReactNode[]) => ReactNode;
|
|
14
|
+
type MapToReactNodeFunction<Params extends Record<string, any>> = {
|
|
15
|
+
[key in keyof Params]: Params[key] extends ParsedFormatFn ? FormatXMLElementReactNodeFn : Params[key];
|
|
16
|
+
};
|
|
17
|
+
type TranslateFn<FormatFnByKey extends ParsedFormatFnByKey> = {
|
|
18
|
+
<TranslationKey extends keyof FormatFnByKey>(key: TranslationKey, params: MapToReactNodeFunction<Parameters<FormatFnByKey[TranslationKey]>[0]>): ReturnType<FormatFnByKey[TranslationKey]> extends string ? string : ReactNode | string | Array<ReactNode | string>;
|
|
19
|
+
<TranslationKey extends keyof FormatFnByKey>(key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<string, any> ? never : TranslationKey): string;
|
|
20
|
+
};
|
|
21
|
+
export declare function useTranslations<Language extends string, FormatFnByKey extends ParsedFormatFnByKey>(translations: TranslationFile<Language, FormatFnByKey>): {
|
|
22
|
+
ready: boolean;
|
|
23
|
+
t: TranslateFn<FormatFnByKey>;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -8,7 +8,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
|
|
|
8
8
|
|
|
9
9
|
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
10
|
|
|
11
|
-
const TranslationsContext = /*#__PURE__*/React__default[
|
|
11
|
+
const TranslationsContext = /*#__PURE__*/React__default["default"].createContext(undefined);
|
|
12
12
|
const VocabProvider = ({
|
|
13
13
|
children,
|
|
14
14
|
language,
|
|
@@ -18,21 +18,18 @@ const VocabProvider = ({
|
|
|
18
18
|
language,
|
|
19
19
|
locale
|
|
20
20
|
}), [language, locale]);
|
|
21
|
-
return /*#__PURE__*/React__default[
|
|
21
|
+
return /*#__PURE__*/React__default["default"].createElement(TranslationsContext.Provider, {
|
|
22
22
|
value: value
|
|
23
23
|
}, children);
|
|
24
24
|
};
|
|
25
25
|
const useLanguage = () => {
|
|
26
26
|
const context = React.useContext(TranslationsContext);
|
|
27
|
-
|
|
28
27
|
if (!context) {
|
|
29
28
|
throw new Error('Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?');
|
|
30
29
|
}
|
|
31
|
-
|
|
32
30
|
if (!context.language) {
|
|
33
31
|
throw new Error('Attempted to access translation without language set. Did you forget to pass language to VocabProvider?');
|
|
34
32
|
}
|
|
35
|
-
|
|
36
33
|
return context;
|
|
37
34
|
};
|
|
38
35
|
const SERVER_RENDERING = typeof window === 'undefined';
|
|
@@ -44,37 +41,29 @@ function useTranslations(translations) {
|
|
|
44
41
|
const [, forceRender] = React.useReducer(s => s + 1, 0);
|
|
45
42
|
const translationsObject = translations.getLoadedMessages(language, locale || language);
|
|
46
43
|
let ready = true;
|
|
47
|
-
|
|
48
44
|
if (!translationsObject) {
|
|
49
45
|
if (SERVER_RENDERING) {
|
|
50
46
|
throw new Error(`Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`);
|
|
51
47
|
}
|
|
52
|
-
|
|
53
48
|
translations.load(language).then(() => {
|
|
54
49
|
forceRender();
|
|
55
50
|
});
|
|
56
51
|
ready = false;
|
|
57
52
|
}
|
|
58
|
-
|
|
59
53
|
const t = React.useCallback((key, params) => {
|
|
60
54
|
if (!translationsObject) {
|
|
61
55
|
return ' ';
|
|
62
56
|
}
|
|
63
|
-
|
|
64
57
|
const message = translationsObject === null || translationsObject === void 0 ? void 0 : translationsObject[key];
|
|
65
|
-
|
|
66
58
|
if (!message) {
|
|
67
59
|
// eslint-disable-next-line no-console
|
|
68
60
|
console.error(`Unable to find translation for key "${key}". Possible keys are ${Object.keys(translationsObject).map(v => `"${v}"`).join(', ')}`);
|
|
69
61
|
return '';
|
|
70
62
|
}
|
|
71
|
-
|
|
72
63
|
const result = message.format(params);
|
|
73
|
-
|
|
74
64
|
if (Array.isArray(result)) {
|
|
75
65
|
for (let i = 0; i < result.length; i++) {
|
|
76
66
|
const item = result[i];
|
|
77
|
-
|
|
78
67
|
if (typeof item === 'object' && item && !item.key && /*#__PURE__*/React.isValidElement(item)) {
|
|
79
68
|
result[i] = /*#__PURE__*/React.cloneElement(item, {
|
|
80
69
|
key: `_vocab-${i}`
|
|
@@ -82,7 +71,6 @@ function useTranslations(translations) {
|
|
|
82
71
|
}
|
|
83
72
|
}
|
|
84
73
|
}
|
|
85
|
-
|
|
86
74
|
return result;
|
|
87
75
|
}, [translationsObject]);
|
|
88
76
|
return {
|
|
@@ -8,7 +8,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
|
|
|
8
8
|
|
|
9
9
|
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
10
|
|
|
11
|
-
const TranslationsContext = /*#__PURE__*/React__default[
|
|
11
|
+
const TranslationsContext = /*#__PURE__*/React__default["default"].createContext(undefined);
|
|
12
12
|
const VocabProvider = ({
|
|
13
13
|
children,
|
|
14
14
|
language,
|
|
@@ -18,21 +18,18 @@ const VocabProvider = ({
|
|
|
18
18
|
language,
|
|
19
19
|
locale
|
|
20
20
|
}), [language, locale]);
|
|
21
|
-
return /*#__PURE__*/React__default[
|
|
21
|
+
return /*#__PURE__*/React__default["default"].createElement(TranslationsContext.Provider, {
|
|
22
22
|
value: value
|
|
23
23
|
}, children);
|
|
24
24
|
};
|
|
25
25
|
const useLanguage = () => {
|
|
26
26
|
const context = React.useContext(TranslationsContext);
|
|
27
|
-
|
|
28
27
|
if (!context) {
|
|
29
28
|
throw new Error('Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?');
|
|
30
29
|
}
|
|
31
|
-
|
|
32
30
|
if (!context.language) {
|
|
33
31
|
throw new Error('Attempted to access translation without language set. Did you forget to pass language to VocabProvider?');
|
|
34
32
|
}
|
|
35
|
-
|
|
36
33
|
return context;
|
|
37
34
|
};
|
|
38
35
|
const SERVER_RENDERING = typeof window === 'undefined';
|
|
@@ -44,37 +41,29 @@ function useTranslations(translations) {
|
|
|
44
41
|
const [, forceRender] = React.useReducer(s => s + 1, 0);
|
|
45
42
|
const translationsObject = translations.getLoadedMessages(language, locale || language);
|
|
46
43
|
let ready = true;
|
|
47
|
-
|
|
48
44
|
if (!translationsObject) {
|
|
49
45
|
if (SERVER_RENDERING) {
|
|
50
46
|
throw new Error(`Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`);
|
|
51
47
|
}
|
|
52
|
-
|
|
53
48
|
translations.load(language).then(() => {
|
|
54
49
|
forceRender();
|
|
55
50
|
});
|
|
56
51
|
ready = false;
|
|
57
52
|
}
|
|
58
|
-
|
|
59
53
|
const t = React.useCallback((key, params) => {
|
|
60
54
|
if (!translationsObject) {
|
|
61
55
|
return ' ';
|
|
62
56
|
}
|
|
63
|
-
|
|
64
57
|
const message = translationsObject === null || translationsObject === void 0 ? void 0 : translationsObject[key];
|
|
65
|
-
|
|
66
58
|
if (!message) {
|
|
67
59
|
// eslint-disable-next-line no-console
|
|
68
60
|
console.error(`Unable to find translation for key "${key}". Possible keys are ${Object.keys(translationsObject).map(v => `"${v}"`).join(', ')}`);
|
|
69
61
|
return '';
|
|
70
62
|
}
|
|
71
|
-
|
|
72
63
|
const result = message.format(params);
|
|
73
|
-
|
|
74
64
|
if (Array.isArray(result)) {
|
|
75
65
|
for (let i = 0; i < result.length; i++) {
|
|
76
66
|
const item = result[i];
|
|
77
|
-
|
|
78
67
|
if (typeof item === 'object' && item && !item.key && /*#__PURE__*/React.isValidElement(item)) {
|
|
79
68
|
result[i] = /*#__PURE__*/React.cloneElement(item, {
|
|
80
69
|
key: `_vocab-${i}`
|
|
@@ -82,7 +71,6 @@ function useTranslations(translations) {
|
|
|
82
71
|
}
|
|
83
72
|
}
|
|
84
73
|
}
|
|
85
|
-
|
|
86
74
|
return result;
|
|
87
75
|
}, [translationsObject]);
|
|
88
76
|
return {
|
package/dist/vocab-react.esm.js
CHANGED
|
@@ -16,15 +16,12 @@ const VocabProvider = ({
|
|
|
16
16
|
};
|
|
17
17
|
const useLanguage = () => {
|
|
18
18
|
const context = useContext(TranslationsContext);
|
|
19
|
-
|
|
20
19
|
if (!context) {
|
|
21
20
|
throw new Error('Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?');
|
|
22
21
|
}
|
|
23
|
-
|
|
24
22
|
if (!context.language) {
|
|
25
23
|
throw new Error('Attempted to access translation without language set. Did you forget to pass language to VocabProvider?');
|
|
26
24
|
}
|
|
27
|
-
|
|
28
25
|
return context;
|
|
29
26
|
};
|
|
30
27
|
const SERVER_RENDERING = typeof window === 'undefined';
|
|
@@ -36,37 +33,29 @@ function useTranslations(translations) {
|
|
|
36
33
|
const [, forceRender] = useReducer(s => s + 1, 0);
|
|
37
34
|
const translationsObject = translations.getLoadedMessages(language, locale || language);
|
|
38
35
|
let ready = true;
|
|
39
|
-
|
|
40
36
|
if (!translationsObject) {
|
|
41
37
|
if (SERVER_RENDERING) {
|
|
42
38
|
throw new Error(`Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`);
|
|
43
39
|
}
|
|
44
|
-
|
|
45
40
|
translations.load(language).then(() => {
|
|
46
41
|
forceRender();
|
|
47
42
|
});
|
|
48
43
|
ready = false;
|
|
49
44
|
}
|
|
50
|
-
|
|
51
45
|
const t = useCallback((key, params) => {
|
|
52
46
|
if (!translationsObject) {
|
|
53
47
|
return ' ';
|
|
54
48
|
}
|
|
55
|
-
|
|
56
49
|
const message = translationsObject === null || translationsObject === void 0 ? void 0 : translationsObject[key];
|
|
57
|
-
|
|
58
50
|
if (!message) {
|
|
59
51
|
// eslint-disable-next-line no-console
|
|
60
52
|
console.error(`Unable to find translation for key "${key}". Possible keys are ${Object.keys(translationsObject).map(v => `"${v}"`).join(', ')}`);
|
|
61
53
|
return '';
|
|
62
54
|
}
|
|
63
|
-
|
|
64
55
|
const result = message.format(params);
|
|
65
|
-
|
|
66
56
|
if (Array.isArray(result)) {
|
|
67
57
|
for (let i = 0; i < result.length; i++) {
|
|
68
58
|
const item = result[i];
|
|
69
|
-
|
|
70
59
|
if (typeof item === 'object' && item && !item.key && /*#__PURE__*/isValidElement(item)) {
|
|
71
60
|
result[i] = /*#__PURE__*/cloneElement(item, {
|
|
72
61
|
key: `_vocab-${i}`
|
|
@@ -74,7 +63,6 @@ function useTranslations(translations) {
|
|
|
74
63
|
}
|
|
75
64
|
}
|
|
76
65
|
}
|
|
77
|
-
|
|
78
66
|
return result;
|
|
79
67
|
}, [translationsObject]);
|
|
80
68
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vocab/react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"main": "dist/vocab-react.cjs.js",
|
|
5
5
|
"module": "dist/vocab-react.esm.js",
|
|
6
6
|
"author": "SEEK",
|
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
"peerDependencies": {
|
|
9
9
|
"react": ">=16.3.0"
|
|
10
10
|
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
11
14
|
"dependencies": {
|
|
12
|
-
"@vocab/
|
|
15
|
+
"@vocab/core": "^1.3.0",
|
|
13
16
|
"intl-messageformat": "^10.0.0"
|
|
14
17
|
},
|
|
15
18
|
"devDependencies": {
|
|
16
19
|
"@types/react": "^18.0.9",
|
|
17
20
|
"react": "^18.1.0"
|
|
18
21
|
}
|
|
19
|
-
}
|
|
22
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
# @vocab/react
|
|
2
|
-
|
|
3
|
-
## 1.1.3
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- [`e5a066c`](https://github.com/seek-oss/vocab/commit/e5a066c8a7539a62a9c1c4d813aa87461ba43cdc) [#96](https://github.com/seek-oss/vocab/pull/96) Thanks [@askoufis](https://github.com/askoufis)! - Update `intl-messageformat` dependencies
|
|
8
|
-
|
|
9
|
-
- Updated dependencies [[`e5a066c`](https://github.com/seek-oss/vocab/commit/e5a066c8a7539a62a9c1c4d813aa87461ba43cdc)]:
|
|
10
|
-
- @vocab/types@1.1.1
|
|
11
|
-
|
|
12
|
-
## 1.1.2
|
|
13
|
-
|
|
14
|
-
### Patch Changes
|
|
15
|
-
|
|
16
|
-
- [`240d6ad`](https://github.com/seek-oss/vocab/commit/240d6ad7e0cf43fed92655a2f95fb463bd7b6644) [#85](https://github.com/seek-oss/vocab/pull/85) Thanks [@askoufis](https://github.com/askoufis)! - The `t` function returned from `useTranslations` is now memoized. `t` should now only change after the initial loading of translations, and when the language changes, making it more useful inside a hook's dependency array.
|
|
17
|
-
|
|
18
|
-
## 1.1.1
|
|
19
|
-
|
|
20
|
-
### Patch Changes
|
|
21
|
-
|
|
22
|
-
- [`e9c7067`](https://github.com/seek-oss/vocab/commit/e9c7067b31215a176e70ac1e73f2c878107f328f) [#83](https://github.com/seek-oss/vocab/pull/83) Thanks [@michaeltaranto](https://github.com/michaeltaranto)! - Add React 18 support
|
|
23
|
-
|
|
24
|
-
## 1.1.0
|
|
25
|
-
|
|
26
|
-
### Minor Changes
|
|
27
|
-
|
|
28
|
-
- [`6de02b3`](https://github.com/seek-oss/vocab/commit/6de02b35839e8ecdd9016fec49a95e17d3696f87) [#69](https://github.com/seek-oss/vocab/pull/69) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Automatically assign keys to React elements
|
|
29
|
-
|
|
30
|
-
Previously, when using React elements within translation templates, React would warn about missing keys as the return type is an array. This meant you needed to supply a key manually. Vocab can now automatically assign a key to React elements. Keys that are passed explicitly will remain untouched.
|
|
31
|
-
|
|
32
|
-
## 1.0.2
|
|
33
|
-
|
|
34
|
-
### Patch Changes
|
|
35
|
-
|
|
36
|
-
- [`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
|
|
37
|
-
|
|
38
|
-
- Updated dependencies [[`3ec6dba`](https://github.com/seek-oss/vocab/commit/3ec6dbaad590299cc33e2d9d4a877576eb05853a)]:
|
|
39
|
-
- @vocab/types@1.0.1
|
|
40
|
-
|
|
41
|
-
## 1.0.1
|
|
42
|
-
|
|
43
|
-
### Patch Changes
|
|
44
|
-
|
|
45
|
-
- [`c9a38dd`](https://github.com/seek-oss/vocab/commit/c9a38dd15e2c2a47fc4d5eb2348fdd08a6982768) [#54](https://github.com/seek-oss/vocab/pull/54) Thanks [@jahredhope](https://github.com/jahredhope)! - Allow special characters within translation keys and messages
|
|
46
|
-
|
|
47
|
-
* [`c9a38dd`](https://github.com/seek-oss/vocab/commit/c9a38dd15e2c2a47fc4d5eb2348fdd08a6982768) [#54](https://github.com/seek-oss/vocab/pull/54) Thanks [@jahredhope](https://github.com/jahredhope)! - Add a warning when accessing a key that doesn't exist
|
|
48
|
-
|
|
49
|
-
## 1.0.0
|
|
50
|
-
|
|
51
|
-
### Major Changes
|
|
52
|
-
|
|
53
|
-
- [`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
|
|
54
|
-
|
|
55
|
-
Release Vocab as v1.0.0 to signify a stable API and support future [semver versioning](https://semver.org/) releases.
|
|
56
|
-
|
|
57
|
-
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.
|
|
58
|
-
|
|
59
|
-
### Patch Changes
|
|
60
|
-
|
|
61
|
-
- [`0074382`](https://github.com/seek-oss/vocab/commit/007438273ef70f5d5ded45777933651ad8df36f6) [#52](https://github.com/seek-oss/vocab/pull/52) Thanks [@jahredhope](https://github.com/jahredhope)! - Remove React dependency on core types.
|
|
62
|
-
|
|
63
|
-
Direct use of tags in Translations now have stricter type definitions.
|
|
64
|
-
|
|
65
|
-
- Updated dependencies [[`0074382`](https://github.com/seek-oss/vocab/commit/007438273ef70f5d5ded45777933651ad8df36f6), [`3031054`](https://github.com/seek-oss/vocab/commit/303105440851db6126f0606e1607745b27dd981c)]:
|
|
66
|
-
- @vocab/types@1.0.0
|
|
67
|
-
|
|
68
|
-
## 0.0.12
|
|
69
|
-
|
|
70
|
-
### Patch Changes
|
|
71
|
-
|
|
72
|
-
- [`5b1fdc0`](https://github.com/seek-oss/vocab/commit/5b1fdc019522b12e7ef94b2fec57b54a9310d41c) [#46](https://github.com/seek-oss/vocab/pull/46) Thanks [@jahredhope](https://github.com/jahredhope)! - Enable the use of translation files directly with 3 new documented methods for working with translations.
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
/**
|
|
76
|
-
* Retrieve messages. If not available, will attempt to load messages and resolve once complete.
|
|
77
|
-
*/
|
|
78
|
-
translations.getMessages(language);
|
|
79
|
-
/**
|
|
80
|
-
* Retrieve already loaded messages. Will return null if messages haven't been loaded.
|
|
81
|
-
*/
|
|
82
|
-
translations.getLoadedMessages(language);
|
|
83
|
-
/**
|
|
84
|
-
* Load messages for the given language. Resolving once complete.
|
|
85
|
-
*/
|
|
86
|
-
translations.load(language);
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
- Updated dependencies [[`5b1fdc0`](https://github.com/seek-oss/vocab/commit/5b1fdc019522b12e7ef94b2fec57b54a9310d41c)]:
|
|
90
|
-
- @vocab/types@0.0.9
|
|
91
|
-
|
|
92
|
-
## 0.0.11
|
|
93
|
-
|
|
94
|
-
### Patch Changes
|
|
95
|
-
|
|
96
|
-
- [`f2fca67`](https://github.com/seek-oss/vocab/commit/f2fca679c66ae65405a0aa24f0a0e472026aad0d) [#36](https://github.com/seek-oss/vocab/pull/36) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Support custom locales for ICU message parsing
|
|
97
|
-
|
|
98
|
-
* [`6c725f4`](https://github.com/seek-oss/vocab/commit/6c725f43c5eaed9b248c8452ff6f83cef1b2f61c) [#35](https://github.com/seek-oss/vocab/pull/35) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Rename `useTranslation` to `useTranslations`
|
|
99
|
-
|
|
100
|
-
* Updated dependencies [[`f2fca67`](https://github.com/seek-oss/vocab/commit/f2fca679c66ae65405a0aa24f0a0e472026aad0d)]:
|
|
101
|
-
- @vocab/types@0.0.8
|
|
102
|
-
|
|
103
|
-
## 0.0.10
|
|
104
|
-
|
|
105
|
-
### Patch Changes
|
|
106
|
-
|
|
107
|
-
- Updated dependencies [[`283bcad`](https://github.com/seek-oss/vocab/commit/283bcada06e622ab14ed891743ed3f55cf09e245), [`f3992ef`](https://github.com/seek-oss/vocab/commit/f3992efbf08939ebf853fac650a49cc46dc51dfb)]:
|
|
108
|
-
- @vocab/types@0.0.7
|
|
109
|
-
|
|
110
|
-
## 0.0.9
|
|
111
|
-
|
|
112
|
-
### Patch Changes
|
|
113
|
-
|
|
114
|
-
- Updated dependencies [[`80a46c0`](https://github.com/seek-oss/vocab/commit/80a46c01a55408675f5822c3618519f80136c3ab)]:
|
|
115
|
-
- @vocab/types@0.0.6
|
|
116
|
-
|
|
117
|
-
## 0.0.8
|
|
118
|
-
|
|
119
|
-
### Patch Changes
|
|
120
|
-
|
|
121
|
-
- [`b51db12`](https://github.com/seek-oss/vocab/commit/b51db125b6d5e29feb77eac20a45b410e79be9b2) [#21](https://github.com/seek-oss/vocab/pull/21) Thanks [@jahredhope](https://github.com/jahredhope)! - Rename TranslationsProvider to VocabProvider
|
|
122
|
-
|
|
123
|
-
## 0.0.7
|
|
124
|
-
|
|
125
|
-
### Patch Changes
|
|
126
|
-
|
|
127
|
-
- [`5f5c581`](https://github.com/seek-oss/vocab/commit/5f5c581a65bff28729ee19e1ec0bdea488a9d6c2) [#19](https://github.com/seek-oss/vocab/pull/19) Thanks [@jahredhope](https://github.com/jahredhope)! - Compile useable TypeScript importable files with `vocab compile`.
|
|
128
|
-
|
|
129
|
-
The new `vocab compile` step replaces `vocab generate-types` in creating a fully functional **translations.ts** file.
|
|
130
|
-
|
|
131
|
-
This allows vocab to be used **without the Webpack Plugin**, however use of the plugin is still heavily advised to ensure optimal loading of translation content on the web.
|
|
132
|
-
|
|
133
|
-
Support for unit testing is now better than ever! The newly created **translations.ts** means your unit test code will see the same code as available while rendering.
|
|
134
|
-
|
|
135
|
-
See the [documentation](https://github.com/seek-oss/vocab) for further usage details.
|
|
136
|
-
|
|
137
|
-
- Updated dependencies [[`5f5c581`](https://github.com/seek-oss/vocab/commit/5f5c581a65bff28729ee19e1ec0bdea488a9d6c2)]:
|
|
138
|
-
- @vocab/types@0.0.5
|
|
139
|
-
|
|
140
|
-
## 0.0.6
|
|
141
|
-
|
|
142
|
-
### Patch Changes
|
|
143
|
-
|
|
144
|
-
- [`26b52f4`](https://github.com/seek-oss/vocab/commit/26b52f4878ded440841e08c858bdc9e685500c2a) [#16](https://github.com/seek-oss/vocab/pull/16) Thanks [@jahredhope](https://github.com/jahredhope)! - Enable debugging with DEBUG environment variable
|
|
145
|
-
|
|
146
|
-
- Updated dependencies [[`08de30d`](https://github.com/seek-oss/vocab/commit/08de30d338c2a5ebdcf14da7c736dddf22e7ca9e)]:
|
|
147
|
-
- @vocab/types@0.0.4
|
|
148
|
-
|
|
149
|
-
## 0.0.5
|
|
150
|
-
|
|
151
|
-
### Patch Changes
|
|
152
|
-
|
|
153
|
-
- [`4710f34`](https://github.com/seek-oss/vocab/commit/4710f341f2827643e3eff69ef7e26d44ec6e8a2b) [#8](https://github.com/seek-oss/vocab/pull/8) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Infer `t` return type more intelligently
|
|
154
|
-
|
|
155
|
-
The translate key function (`t`) will now infer the return type as ReactNode only when the tag syntax is used.
|
|
156
|
-
|
|
157
|
-
- Updated dependencies [[`4710f34`](https://github.com/seek-oss/vocab/commit/4710f341f2827643e3eff69ef7e26d44ec6e8a2b)]:
|
|
158
|
-
- @vocab/types@0.0.3
|
|
159
|
-
|
|
160
|
-
## 0.0.4
|
|
161
|
-
|
|
162
|
-
### Patch Changes
|
|
163
|
-
|
|
164
|
-
- [`45c4fe2`](https://github.com/seek-oss/vocab/commit/45c4fe273c5157475cb03ca57db662956ad5cbc9) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Improved type definitions for `t` function
|
|
165
|
-
|
|
166
|
-
## 0.0.3
|
|
167
|
-
|
|
168
|
-
### Patch Changes
|
|
169
|
-
|
|
170
|
-
- [`f79c85e`](https://github.com/seek-oss/vocab/commit/f79c85e37c5b927306866961cf6cb3c541d0d6cf) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Add @vocab/types dep
|
|
171
|
-
|
|
172
|
-
## 0.0.2
|
|
173
|
-
|
|
174
|
-
### Patch Changes
|
|
175
|
-
|
|
176
|
-
- [`9f99ea7`](https://github.com/seek-oss/vocab/commit/9f99ea7c827ec4d7c21a485e17e3adbbd1c49319) Thanks [@jahredhope](https://github.com/jahredhope)! - Remove React as dependency and target node
|
package/src/index.tsx
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TranslationFile,
|
|
3
|
-
LanguageName,
|
|
4
|
-
ParsedFormatFnByKey,
|
|
5
|
-
ParsedFormatFn,
|
|
6
|
-
} from '@vocab/types';
|
|
7
|
-
import React, {
|
|
8
|
-
ReactNode,
|
|
9
|
-
useContext,
|
|
10
|
-
useMemo,
|
|
11
|
-
useReducer,
|
|
12
|
-
isValidElement,
|
|
13
|
-
cloneElement,
|
|
14
|
-
useCallback,
|
|
15
|
-
} from 'react';
|
|
16
|
-
|
|
17
|
-
type Locale = string;
|
|
18
|
-
|
|
19
|
-
interface TranslationsValue {
|
|
20
|
-
language: LanguageName;
|
|
21
|
-
locale?: Locale;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const TranslationsContext = React.createContext<TranslationsValue | undefined>(
|
|
25
|
-
undefined,
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
interface VocabProviderProps extends TranslationsValue {
|
|
29
|
-
children: ReactNode;
|
|
30
|
-
}
|
|
31
|
-
export const VocabProvider = ({
|
|
32
|
-
children,
|
|
33
|
-
language,
|
|
34
|
-
locale,
|
|
35
|
-
}: VocabProviderProps) => {
|
|
36
|
-
const value = useMemo(() => ({ language, locale }), [language, locale]);
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<TranslationsContext.Provider value={value}>
|
|
40
|
-
{children}
|
|
41
|
-
</TranslationsContext.Provider>
|
|
42
|
-
);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const useLanguage = (): TranslationsValue => {
|
|
46
|
-
const context = useContext(TranslationsContext);
|
|
47
|
-
if (!context) {
|
|
48
|
-
throw new Error(
|
|
49
|
-
'Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?',
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
if (!context.language) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
'Attempted to access translation without language set. Did you forget to pass language to VocabProvider?',
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return context;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const SERVER_RENDERING = typeof window === 'undefined';
|
|
62
|
-
|
|
63
|
-
type FormatXMLElementReactNodeFn = (parts: ReactNode[]) => ReactNode;
|
|
64
|
-
|
|
65
|
-
type MapToReactNodeFunction<Params extends Record<string, any>> = {
|
|
66
|
-
[key in keyof Params]: Params[key] extends ParsedFormatFn
|
|
67
|
-
? FormatXMLElementReactNodeFn
|
|
68
|
-
: Params[key];
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
type TranslateFn<FormatFnByKey extends ParsedFormatFnByKey> = {
|
|
72
|
-
<TranslationKey extends keyof FormatFnByKey>(
|
|
73
|
-
key: TranslationKey,
|
|
74
|
-
params: MapToReactNodeFunction<
|
|
75
|
-
Parameters<FormatFnByKey[TranslationKey]>[0]
|
|
76
|
-
>,
|
|
77
|
-
): ReturnType<FormatFnByKey[TranslationKey]> extends string
|
|
78
|
-
? string
|
|
79
|
-
: ReactNode | string | Array<ReactNode | string>;
|
|
80
|
-
<TranslationKey extends keyof FormatFnByKey>(
|
|
81
|
-
key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<
|
|
82
|
-
string,
|
|
83
|
-
any
|
|
84
|
-
>
|
|
85
|
-
? never
|
|
86
|
-
: TranslationKey,
|
|
87
|
-
): string;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export function useTranslations<
|
|
91
|
-
Language extends string,
|
|
92
|
-
FormatFnByKey extends ParsedFormatFnByKey,
|
|
93
|
-
>(
|
|
94
|
-
translations: TranslationFile<Language, FormatFnByKey>,
|
|
95
|
-
): {
|
|
96
|
-
ready: boolean;
|
|
97
|
-
t: TranslateFn<FormatFnByKey>;
|
|
98
|
-
} {
|
|
99
|
-
const { language, locale } = useLanguage();
|
|
100
|
-
const [, forceRender] = useReducer((s: number) => s + 1, 0);
|
|
101
|
-
|
|
102
|
-
const translationsObject = translations.getLoadedMessages(
|
|
103
|
-
language as any,
|
|
104
|
-
locale || language,
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
let ready = true;
|
|
108
|
-
|
|
109
|
-
if (!translationsObject) {
|
|
110
|
-
if (SERVER_RENDERING) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
translations.load(language as any).then(() => {
|
|
117
|
-
forceRender();
|
|
118
|
-
});
|
|
119
|
-
ready = false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const t = useCallback(
|
|
123
|
-
(key: string, params?: any) => {
|
|
124
|
-
if (!translationsObject) {
|
|
125
|
-
return ' ';
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const message = translationsObject?.[key];
|
|
129
|
-
|
|
130
|
-
if (!message) {
|
|
131
|
-
// eslint-disable-next-line no-console
|
|
132
|
-
console.error(
|
|
133
|
-
`Unable to find translation for key "${key}". Possible keys are ${Object.keys(
|
|
134
|
-
translationsObject,
|
|
135
|
-
)
|
|
136
|
-
.map((v) => `"${v}"`)
|
|
137
|
-
.join(', ')}`,
|
|
138
|
-
);
|
|
139
|
-
return '';
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const result = message.format(params);
|
|
143
|
-
|
|
144
|
-
if (Array.isArray(result)) {
|
|
145
|
-
for (let i = 0; i < result.length; i++) {
|
|
146
|
-
const item = result[i];
|
|
147
|
-
if (
|
|
148
|
-
typeof item === 'object' &&
|
|
149
|
-
item &&
|
|
150
|
-
!item.key &&
|
|
151
|
-
isValidElement(item)
|
|
152
|
-
) {
|
|
153
|
-
result[i] = cloneElement(item, { key: `_vocab-${i}` });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return result;
|
|
159
|
-
},
|
|
160
|
-
[translationsObject],
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
ready,
|
|
165
|
-
t,
|
|
166
|
-
};
|
|
167
|
-
}
|