@jetshop/intl 5.13.2 → 5.13.12-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.
package/babel.js CHANGED
@@ -1,23 +1,23 @@
1
- const env = process.env.BABEL_ENV || process.env.NODE_ENV;
2
- if (env !== 'development' && env !== 'test' && env !== 'production') {
3
- throw new Error(
4
- 'Using `babel-preset-jetshop-intl` requires that you specify `NODE_ENV` ' +
5
- 'or `BABEL_ENV` environment variables. Valid values are "development", ' +
6
- '"test", and "production". Instead, received: ' +
7
- JSON.stringify(env) +
8
- '.'
9
- );
10
- }
11
-
12
- module.exports = function(context, opts) {
13
- return {
14
- plugins: [
15
- [
16
- require.resolve('babel-plugin-transform-format-message'),
17
- {
18
- generateId: 'underscored_crc32'
19
- }
20
- ]
21
- ]
22
- };
23
- };
1
+ const env = process.env.BABEL_ENV || process.env.NODE_ENV;
2
+ if (env !== 'development' && env !== 'test' && env !== 'production') {
3
+ throw new Error(
4
+ 'Using `babel-preset-jetshop-intl` requires that you specify `NODE_ENV` ' +
5
+ 'or `BABEL_ENV` environment variables. Valid values are "development", ' +
6
+ '"test", and "production". Instead, received: ' +
7
+ JSON.stringify(env) +
8
+ '.'
9
+ );
10
+ }
11
+
12
+ module.exports = function(context, opts) {
13
+ return {
14
+ plugins: [
15
+ [
16
+ require.resolve('babel-plugin-transform-format-message'),
17
+ {
18
+ generateId: 'underscored_crc32'
19
+ }
20
+ ]
21
+ ]
22
+ };
23
+ };
package/bin/extract.js CHANGED
@@ -1,21 +1,21 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable @typescript-eslint/no-var-requires */
3
-
4
- const program = require('format-message-cli');
5
- // TODO: Support typescript
6
- program.parse(
7
- process.argv
8
- .slice(0, 2)
9
- .concat([
10
- 'extract',
11
- 'src/**/*.js',
12
- 'node_modules/@jetshop/ui/**/*.js',
13
- 'node_modules/@jetshop/core/**/*.js',
14
- '../ui/**/*.js',
15
- '../core/**/*.js',
16
- '-g',
17
- 'underscored_crc32',
18
- '-o',
19
- 'translations/default.json'
20
- ])
21
- );
1
+ #!/usr/bin/env node
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
3
+
4
+ const program = require('format-message-cli');
5
+ // TODO: Support typescript
6
+ program.parse(
7
+ process.argv
8
+ .slice(0, 2)
9
+ .concat([
10
+ 'extract',
11
+ 'src/**/*.js',
12
+ 'node_modules/@jetshop/ui/**/*.js',
13
+ 'node_modules/@jetshop/core/**/*.js',
14
+ '../ui/**/*.js',
15
+ '../core/**/*.js',
16
+ '-g',
17
+ 'underscored_crc32',
18
+ '-o',
19
+ 'translations/default.json'
20
+ ])
21
+ );
package/bin/intl.js CHANGED
@@ -1,34 +1,34 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable @typescript-eslint/no-var-requires */
3
-
4
- process.env.NODE_ENV = 'production';
5
- process.env.MODULE_NAME_PATTERN = '@jetshop/intl';
6
-
7
- require('@jetshop/react-scripts/config/env');
8
- const spawn = require('react-dev-utils/crossSpawn');
9
- const args = process.argv.slice(2);
10
-
11
- const scriptIndex = args.findIndex(x => x === 'extract' || x === 'lint');
12
- const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
13
- const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];
14
-
15
- switch (script) {
16
- case 'extract':
17
- case 'lint': {
18
- const result = spawn.sync(
19
- 'node',
20
- nodeArgs
21
- .concat(require.resolve('./' + script))
22
- .concat(args.slice(scriptIndex + 1)),
23
- { stdio: 'inherit' }
24
- );
25
- if (result.signal) {
26
- process.exit(1);
27
- }
28
- process.exit(result.status);
29
- break;
30
- }
31
- default:
32
- console.log('Unknown script "' + script + '".');
33
- break;
34
- }
1
+ #!/usr/bin/env node
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
3
+
4
+ process.env.NODE_ENV = 'production';
5
+ process.env.MODULE_NAME_PATTERN = '@jetshop/intl';
6
+
7
+ require('@jetshop/react-scripts/config/env');
8
+ const spawn = require('react-dev-utils/crossSpawn');
9
+ const args = process.argv.slice(2);
10
+
11
+ const scriptIndex = args.findIndex(x => x === 'extract' || x === 'lint');
12
+ const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
13
+ const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];
14
+
15
+ switch (script) {
16
+ case 'extract':
17
+ case 'lint': {
18
+ const result = spawn.sync(
19
+ 'node',
20
+ nodeArgs
21
+ .concat(require.resolve('./' + script))
22
+ .concat(args.slice(scriptIndex + 1)),
23
+ { stdio: 'inherit' }
24
+ );
25
+ if (result.signal) {
26
+ process.exit(1);
27
+ }
28
+ process.exit(result.status);
29
+ break;
30
+ }
31
+ default:
32
+ console.log('Unknown script "' + script + '".');
33
+ break;
34
+ }
package/bin/lint.js CHANGED
@@ -1,87 +1,87 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable @typescript-eslint/no-var-requires */
3
-
4
- const fs = require('fs');
5
- const glob = require('glob');
6
- const CLIEngine = require('eslint').CLIEngine;
7
- const {
8
- uiPath,
9
- corePath,
10
- intlPath
11
- } = require('@jetshop/react-scripts/config/paths');
12
-
13
- const translationFiles = glob.sync('translations/*.json');
14
- const srcFiles = [
15
- ...glob.sync('src/**/*.{js,ts,tsx}'),
16
- ...glob.sync(`${uiPath}/**/*.{js,ts,tsx}`),
17
- ...glob.sync(`${corePath}/**/*.{js,ts,tsx}`)
18
- ];
19
-
20
- const localeRegexp = /([a-z-_A-Z]+)\.json/;
21
-
22
- const getLocaleFromFilename = filename => {
23
- if (localeRegexp.test(filename)) {
24
- return filename.match(localeRegexp)[1];
25
- } else {
26
- throw new Error('Could not figure out locale for ' + filename);
27
- }
28
- };
29
-
30
- const translations = translationFiles.reduce((acc, file) => {
31
- const locale = getLocaleFromFilename(file);
32
-
33
- // Skip linting the default.json file
34
- if (locale === 'default') {
35
- return acc;
36
- }
37
-
38
- const data = JSON.parse(fs.readFileSync(file, 'utf8'));
39
- return {
40
- ...acc,
41
- [locale]: data
42
- };
43
- }, {});
44
-
45
- var cli = new CLIEngine({
46
- useEslintrc: false,
47
- ignorePath: `${intlPath}/bin/.eslintignore`,
48
- envs: ['es6', 'browser', 'node'],
49
- baseConfig: {
50
- parser: '@typescript-eslint/parser',
51
- parserOptions: {
52
- ecmaVersion: 6,
53
- sourceType: 'module',
54
- ecmaFeatures: {
55
- modules: true,
56
- experimentalObjectRestSpread: true,
57
- jsx: true
58
- }
59
- },
60
- plugins: ['format-message', '@typescript-eslint'],
61
- settings: {
62
- 'format-message': {
63
- generateId: 'underscored_crc32',
64
- translations
65
- }
66
- },
67
- extends: 'plugin:format-message/default'
68
- }
69
- });
70
-
71
- const report = {
72
- results: [],
73
- errorCount: 0,
74
- warningCount: 0
75
- };
76
-
77
- srcFiles.forEach(file => {
78
- const data = fs.readFileSync(file, 'utf8');
79
- const fileReport = cli.executeOnText(data, file);
80
- report.results = report.results.concat(fileReport.results);
81
- report.errorCount += fileReport.errorCount;
82
- report.warningCount += fileReport.warningCount;
83
- });
84
-
85
- const format = cli.getFormatter('stylish');
86
- const formatted = format(report.results);
87
- if (formatted) console.error(formatted);
1
+ #!/usr/bin/env node
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
3
+
4
+ const fs = require('fs');
5
+ const glob = require('glob');
6
+ const CLIEngine = require('eslint').CLIEngine;
7
+ const {
8
+ uiPath,
9
+ corePath,
10
+ intlPath
11
+ } = require('@jetshop/react-scripts/config/paths');
12
+
13
+ const translationFiles = glob.sync('translations/*.json');
14
+ const srcFiles = [
15
+ ...glob.sync('src/**/*.{js,ts,tsx}'),
16
+ ...glob.sync(`${uiPath}/**/*.{js,ts,tsx}`),
17
+ ...glob.sync(`${corePath}/**/*.{js,ts,tsx}`)
18
+ ];
19
+
20
+ const localeRegexp = /([a-z-_A-Z]+)\.json/;
21
+
22
+ const getLocaleFromFilename = filename => {
23
+ if (localeRegexp.test(filename)) {
24
+ return filename.match(localeRegexp)[1];
25
+ } else {
26
+ throw new Error('Could not figure out locale for ' + filename);
27
+ }
28
+ };
29
+
30
+ const translations = translationFiles.reduce((acc, file) => {
31
+ const locale = getLocaleFromFilename(file);
32
+
33
+ // Skip linting the default.json file
34
+ if (locale === 'default') {
35
+ return acc;
36
+ }
37
+
38
+ const data = JSON.parse(fs.readFileSync(file, 'utf8'));
39
+ return {
40
+ ...acc,
41
+ [locale]: data
42
+ };
43
+ }, {});
44
+
45
+ var cli = new CLIEngine({
46
+ useEslintrc: false,
47
+ ignorePath: `${intlPath}/bin/.eslintignore`,
48
+ envs: ['es6', 'browser', 'node'],
49
+ baseConfig: {
50
+ parser: '@typescript-eslint/parser',
51
+ parserOptions: {
52
+ ecmaVersion: 6,
53
+ sourceType: 'module',
54
+ ecmaFeatures: {
55
+ modules: true,
56
+ experimentalObjectRestSpread: true,
57
+ jsx: true
58
+ }
59
+ },
60
+ plugins: ['format-message', '@typescript-eslint'],
61
+ settings: {
62
+ 'format-message': {
63
+ generateId: 'underscored_crc32',
64
+ translations
65
+ }
66
+ },
67
+ extends: 'plugin:format-message/default'
68
+ }
69
+ });
70
+
71
+ const report = {
72
+ results: [],
73
+ errorCount: 0,
74
+ warningCount: 0
75
+ };
76
+
77
+ srcFiles.forEach(file => {
78
+ const data = fs.readFileSync(file, 'utf8');
79
+ const fileReport = cli.executeOnText(data, file);
80
+ report.results = report.results.concat(fileReport.results);
81
+ report.errorCount += fileReport.errorCount;
82
+ report.warningCount += fileReport.warningCount;
83
+ });
84
+
85
+ const format = cli.getFormatter('stylish');
86
+ const formatted = format(report.results);
87
+ if (formatted) console.error(formatted);
package/config.js CHANGED
@@ -1,119 +1,119 @@
1
- import formatMessage from 'format-message';
2
- import underscoredCrc32 from 'format-message-generate-id/underscored_crc32';
3
-
4
- // Cache translation files
5
- const cachedTranslationPromises = {};
6
-
7
- const createTranslator = ({
8
- locale,
9
- fallback,
10
- messages,
11
- fallbackMessages,
12
- options
13
- }) => {
14
- const translator = __IN_SERVER__ ? formatMessage.namespace() : formatMessage;
15
-
16
- translator.setup({
17
- translations: {
18
- [locale]: messages,
19
- [fallback]: fallbackMessages
20
- },
21
- locale: [locale, fallback],
22
- generateId: underscoredCrc32,
23
- ...options
24
- });
25
-
26
- return translator;
27
- };
28
-
29
- /**
30
- * Takes a locale, e.g. 'sv-SE' and resolves it to the corresponding key in the translations
31
- * @param {string} locale Example: `sv-SE`
32
- * @param {object} translations
33
- * @return {string} Example: `sv`
34
- */
35
- export const getLocaleName = (locale, translations) => {
36
- if (!locale) {
37
- return null;
38
- }
39
-
40
- if (locale in translations) {
41
- return locale;
42
- }
43
-
44
- const lowerCase = locale.toLowerCase();
45
- if (lowerCase in translations) {
46
- return lowerCase;
47
- }
48
-
49
- const baseLocale = lowerCase.split(/[_-]/)[0];
50
- if (baseLocale in translations) {
51
- return baseLocale;
52
- }
53
-
54
- return null;
55
- };
56
-
57
- export const getLocaleWithFallback = ({
58
- locale,
59
- defaultLocale,
60
- translations
61
- }) => {
62
- const fallback = getLocaleName(defaultLocale, translations);
63
- const loc = getLocaleName(locale, translations);
64
-
65
- if (!fallback) {
66
- throw new Error(
67
- `Default locale "${defaultLocale}" could not be found in translations`
68
- );
69
- }
70
- return {
71
- locale: loc || fallback,
72
- fallback
73
- };
74
- };
75
-
76
- export const loadLocaleMessages = (locale, translations) => {
77
- if (!__IN_SERVER__) {
78
- return translations[locale]();
79
- }
80
- if (!cachedTranslationPromises[locale]) {
81
- cachedTranslationPromises[locale] = translations[locale]();
82
- }
83
-
84
- return cachedTranslationPromises[locale];
85
- };
86
-
87
- export const setupIntl = async config => {
88
- if (!config.translations) {
89
- throw new Error('No translation data provided to translation setup.');
90
- }
91
-
92
- if (!config.defaultLocale) {
93
- throw new Error('No default locale provided to translation setup.');
94
- }
95
-
96
- const translations = config.translations;
97
- const options = config.options || {};
98
-
99
- const { locale, fallback } = getLocaleWithFallback({
100
- locale: config.locale,
101
- defaultLocale: config.defaultLocale,
102
- translations
103
- });
104
-
105
- const [messages, fallbackMessages] = await Promise.all([
106
- loadLocaleMessages(locale, translations),
107
- loadLocaleMessages(fallback, translations)
108
- ]);
109
-
110
- const translator = createTranslator({
111
- locale,
112
- fallback,
113
- messages,
114
- fallbackMessages,
115
- options
116
- });
117
-
118
- return { locale, translator, fallback };
119
- };
1
+ import formatMessage from 'format-message';
2
+ import underscoredCrc32 from 'format-message-generate-id/underscored_crc32';
3
+
4
+ // Cache translation files
5
+ const cachedTranslationPromises = {};
6
+
7
+ const createTranslator = ({
8
+ locale,
9
+ fallback,
10
+ messages,
11
+ fallbackMessages,
12
+ options
13
+ }) => {
14
+ const translator = __IN_SERVER__ ? formatMessage.namespace() : formatMessage;
15
+
16
+ translator.setup({
17
+ translations: {
18
+ [locale]: messages,
19
+ [fallback]: fallbackMessages
20
+ },
21
+ locale: [locale, fallback],
22
+ generateId: underscoredCrc32,
23
+ ...options
24
+ });
25
+
26
+ return translator;
27
+ };
28
+
29
+ /**
30
+ * Takes a locale, e.g. 'sv-SE' and resolves it to the corresponding key in the translations
31
+ * @param {string} locale Example: `sv-SE`
32
+ * @param {object} translations
33
+ * @return {string} Example: `sv`
34
+ */
35
+ export const getLocaleName = (locale, translations) => {
36
+ if (!locale) {
37
+ return null;
38
+ }
39
+
40
+ if (locale in translations) {
41
+ return locale;
42
+ }
43
+
44
+ const lowerCase = locale.toLowerCase();
45
+ if (lowerCase in translations) {
46
+ return lowerCase;
47
+ }
48
+
49
+ const baseLocale = lowerCase.split(/[_-]/)[0];
50
+ if (baseLocale in translations) {
51
+ return baseLocale;
52
+ }
53
+
54
+ return null;
55
+ };
56
+
57
+ export const getLocaleWithFallback = ({
58
+ locale,
59
+ defaultLocale,
60
+ translations
61
+ }) => {
62
+ const fallback = getLocaleName(defaultLocale, translations);
63
+ const loc = getLocaleName(locale, translations);
64
+
65
+ if (!fallback) {
66
+ throw new Error(
67
+ `Default locale "${defaultLocale}" could not be found in translations`
68
+ );
69
+ }
70
+ return {
71
+ locale: loc || fallback,
72
+ fallback
73
+ };
74
+ };
75
+
76
+ export const loadLocaleMessages = (locale, translations) => {
77
+ if (!__IN_SERVER__) {
78
+ return translations[locale]();
79
+ }
80
+ if (!cachedTranslationPromises[locale]) {
81
+ cachedTranslationPromises[locale] = translations[locale]();
82
+ }
83
+
84
+ return cachedTranslationPromises[locale];
85
+ };
86
+
87
+ export const setupIntl = async config => {
88
+ if (!config.translations) {
89
+ throw new Error('No translation data provided to translation setup.');
90
+ }
91
+
92
+ if (!config.defaultLocale) {
93
+ throw new Error('No default locale provided to translation setup.');
94
+ }
95
+
96
+ const translations = config.translations;
97
+ const options = config.options || {};
98
+
99
+ const { locale, fallback } = getLocaleWithFallback({
100
+ locale: config.locale,
101
+ defaultLocale: config.defaultLocale,
102
+ translations
103
+ });
104
+
105
+ const [messages, fallbackMessages] = await Promise.all([
106
+ loadLocaleMessages(locale, translations),
107
+ loadLocaleMessages(fallback, translations)
108
+ ]);
109
+
110
+ const translator = createTranslator({
111
+ locale,
112
+ fallback,
113
+ messages,
114
+ fallbackMessages,
115
+ options
116
+ });
117
+
118
+ return { locale, translator, fallback };
119
+ };
package/context.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { createContext } from 'react';
2
- import formatMessage from 'format-message';
3
-
4
- const IntlContext = createContext(formatMessage);
5
- export default IntlContext;
1
+ import { createContext } from 'react';
2
+ import formatMessage from 'format-message';
3
+
4
+ const IntlContext = createContext(formatMessage);
5
+ export default IntlContext;
package/index.mdx CHANGED
@@ -1,101 +1,101 @@
1
- ---
2
- name: Introduction
3
- menu: '@jetshop/intl'
4
- route: /intl/introduction
5
- ---
6
-
7
- # Introduction
8
-
9
- Localization and translations are essential for building a good cross-border e-commerce, and in Flight there's some tools to help you with that.
10
-
11
- ## Translations
12
-
13
- Much of the content in Flight stores come from the API, which already handles translations. However, there's many cases where you want to keep the texts inside the shop repository, or where you want to do some kind of formatting before rendering the text.
14
- For this we have a wrapper around the library [format-message](https://github.com/format-message/format-message). The wrapper around format-message is needed to use the correct translations when doing server-side rendering.
15
-
16
- When you want to render a translation string you should preferrably use the `useIntl` hook to wrap your translation strings:
17
-
18
- ```javascript
19
- import { useIntl } from '@jetshop/intl';
20
-
21
- const App = () => {
22
- const t = useIntl();
23
-
24
- return <button>{t('This is my translatable button text')}</button>;
25
- };
26
- ```
27
-
28
- This will result in a pure text string rendered where you call the function. There are lots of places in Flight where we use an older way of rendering translation strings, importing `t` directly from `@jetshop/intl`.
29
- This works, but is not recommended, since it has some special behaviour that might cause issues in some places.
30
-
31
- ```javascript
32
- import t from '@jetshop/intl';
33
-
34
- const App = () => {
35
- return <button>{t('This is my translatable button text')}</button>;
36
- }
37
-
38
- // what is actually being rendered
39
- const App = () => {
40
- return <button><IntlContext.Consumer>{t => t('This is my translatable button text')}</IntlContext></button>
41
- }
42
- ```
43
-
44
- In most cases this works well, but if you try to use a translation string inside e.g. a input placeholder, you will end up with broken markup.
45
-
46
- You can also work with inline placeholders:
47
-
48
- ```javascript
49
- import { useIntl } from '@jetshop/intl';
50
-
51
- const App = () => {
52
- const t = useIntl();
53
- return <span>{t('Hi {name}! How are you doing?', { name: 'User' })}</span>;
54
- };
55
- ```
56
-
57
- The [ICU Message Format](http://userguide.icu-project.org/formatparse/messages) that format-message uses, also support pluralization:
58
-
59
- ```javascript
60
- import { useIntl } from '@jetshop/intl';
61
-
62
- const App = () => {
63
- const t = useIntl();
64
- const [count, setCount] = useState(0);
65
- return (
66
- <button onClick={() => setCount(count + 1)}>
67
- {t(
68
- `{count, plural,
69
- =0 {You have not pressed the button}
70
- one {You have pressed the button one time}
71
- other {You have pressed the button # times}
72
- }`,
73
- { count }
74
- )}
75
- </button>
76
- );
77
- };
78
- ```
79
-
80
- Finally it's also possible to render your own components for inline styling using `t.rich`:
81
-
82
- ```javascript
83
- import t from '@jetshop/intl';
84
-
85
- const Important = ({ children }) => <strong key="important">{children}</strong>;
86
-
87
- const App = () => (
88
- <span>
89
- {t.rich('This text is <Important>very important!</Important>', {
90
- Important
91
- })}
92
- </span>
93
- );
94
- ```
95
-
96
- Note that this does not work with the `useIntl` import.
97
-
98
- ## Localization
99
-
100
- format-message can also help with formatting text according to local standards, such as date, time, numbers etc. It's basically a wrapper around the built in Intl functions in the browser and can be used either together with translation strings as a part of the ICU message format, or by using the exposed helper functions on its own.
101
- See [these examples](https://github.com/format-message/format-message/tree/master/packages/format-message#complex-number-date-and-time-placeholders) on the format-message documentation.
1
+ ---
2
+ name: Introduction
3
+ menu: '@jetshop/intl'
4
+ route: /intl/introduction
5
+ ---
6
+
7
+ # Introduction
8
+
9
+ Localization and translations are essential for building a good cross-border e-commerce, and in Flight there's some tools to help you with that.
10
+
11
+ ## Translations
12
+
13
+ Much of the content in Flight stores come from the API, which already handles translations. However, there's many cases where you want to keep the texts inside the shop repository, or where you want to do some kind of formatting before rendering the text.
14
+ For this we have a wrapper around the library [format-message](https://github.com/format-message/format-message). The wrapper around format-message is needed to use the correct translations when doing server-side rendering.
15
+
16
+ When you want to render a translation string you should preferrably use the `useIntl` hook to wrap your translation strings:
17
+
18
+ ```javascript
19
+ import { useIntl } from '@jetshop/intl';
20
+
21
+ const App = () => {
22
+ const t = useIntl();
23
+
24
+ return <button>{t('This is my translatable button text')}</button>;
25
+ };
26
+ ```
27
+
28
+ This will result in a pure text string rendered where you call the function. There are lots of places in Flight where we use an older way of rendering translation strings, importing `t` directly from `@jetshop/intl`.
29
+ This works, but is not recommended, since it has some special behaviour that might cause issues in some places.
30
+
31
+ ```javascript
32
+ import t from '@jetshop/intl';
33
+
34
+ const App = () => {
35
+ return <button>{t('This is my translatable button text')}</button>;
36
+ }
37
+
38
+ // what is actually being rendered
39
+ const App = () => {
40
+ return <button><IntlContext.Consumer>{t => t('This is my translatable button text')}</IntlContext></button>
41
+ }
42
+ ```
43
+
44
+ In most cases this works well, but if you try to use a translation string inside e.g. a input placeholder, you will end up with broken markup.
45
+
46
+ You can also work with inline placeholders:
47
+
48
+ ```javascript
49
+ import { useIntl } from '@jetshop/intl';
50
+
51
+ const App = () => {
52
+ const t = useIntl();
53
+ return <span>{t('Hi {name}! How are you doing?', { name: 'User' })}</span>;
54
+ };
55
+ ```
56
+
57
+ The [ICU Message Format](http://userguide.icu-project.org/formatparse/messages) that format-message uses, also support pluralization:
58
+
59
+ ```javascript
60
+ import { useIntl } from '@jetshop/intl';
61
+
62
+ const App = () => {
63
+ const t = useIntl();
64
+ const [count, setCount] = useState(0);
65
+ return (
66
+ <button onClick={() => setCount(count + 1)}>
67
+ {t(
68
+ `{count, plural,
69
+ =0 {You have not pressed the button}
70
+ one {You have pressed the button one time}
71
+ other {You have pressed the button # times}
72
+ }`,
73
+ { count }
74
+ )}
75
+ </button>
76
+ );
77
+ };
78
+ ```
79
+
80
+ Finally it's also possible to render your own components for inline styling using `t.rich`:
81
+
82
+ ```javascript
83
+ import t from '@jetshop/intl';
84
+
85
+ const Important = ({ children }) => <strong key="important">{children}</strong>;
86
+
87
+ const App = () => (
88
+ <span>
89
+ {t.rich('This text is <Important>very important!</Important>', {
90
+ Important
91
+ })}
92
+ </span>
93
+ );
94
+ ```
95
+
96
+ Note that this does not work with the `useIntl` import.
97
+
98
+ ## Localization
99
+
100
+ format-message can also help with formatting text according to local standards, such as date, time, numbers etc. It's basically a wrapper around the built in Intl functions in the browser and can be used either together with translation strings as a part of the ICU message format, or by using the exposed helper functions on its own.
101
+ See [these examples](https://github.com/format-message/format-message/tree/master/packages/format-message#complex-number-date-and-time-placeholders) on the format-message documentation.
package/index.ts CHANGED
@@ -1,66 +1,66 @@
1
- import React, { useContext } from 'react';
2
- import IntlContext from '@jetshop/intl/context';
3
- import formatMessage from 'format-message/types';
4
-
5
- export const useIntl = () => useContext(IntlContext);
6
-
7
- type FormatMsgType = typeof formatMessage;
8
- type FormatMsgTypes =
9
- | FormatMsgType
10
- | FormatMsgType['rich']
11
- | FormatMsgType['number']
12
- | FormatMsgType['date']
13
- | FormatMsgType['time']
14
- | FormatMsgType['select']
15
- | FormatMsgType['plural']
16
- | FormatMsgType['selectordinal']
17
- | FormatMsgType['custom'];
18
-
19
- type WithContextFormatMessage = ReturnType<typeof withContextFormatMessage>;
20
-
21
- interface TranslateType extends WithContextFormatMessage {
22
- rich: (
23
- ...args: Parameters<FormatMsgType['rich']>
24
- ) => ReturnType<WithContextFormatMessage>;
25
- number: (
26
- ...args: Parameters<FormatMsgType['number']>
27
- ) => ReturnType<WithContextFormatMessage>;
28
- date: (
29
- ...args: Parameters<FormatMsgType['date']>
30
- ) => ReturnType<WithContextFormatMessage>;
31
- time: (
32
- ...args: Parameters<FormatMsgType['time']>
33
- ) => ReturnType<WithContextFormatMessage>;
34
- select: (
35
- ...args: Parameters<FormatMsgType['select']>
36
- ) => ReturnType<WithContextFormatMessage>;
37
- plural: (
38
- ...args: Parameters<FormatMsgType['plural']>
39
- ) => ReturnType<WithContextFormatMessage>;
40
- selectordinal: (
41
- ...args: Parameters<FormatMsgType['selectordinal']>
42
- ) => ReturnType<WithContextFormatMessage>;
43
- custom: (
44
- ...args: Parameters<FormatMsgType['custom']>
45
- ) => ReturnType<WithContextFormatMessage>;
46
- }
47
-
48
- const withContextFormatMessage =
49
- (callback: (t: FormatMsgType) => ReturnType<FormatMsgTypes>) =>
50
- (...args: Parameters<FormatMsgTypes>) =>
51
- React.createElement(IntlContext.Consumer, null, (t: FormatMsgType) =>
52
- callback(t)(...args)
53
- );
54
-
55
- const Translate = withContextFormatMessage((t) => t) as TranslateType;
56
- Translate.rich = withContextFormatMessage((t) => t.rich);
57
- Translate.number = withContextFormatMessage((t) => t.number);
58
- Translate.date = withContextFormatMessage((t) => t.date);
59
- Translate.time = withContextFormatMessage((t) => t.time);
60
- Translate.select = withContextFormatMessage((t) => t.select);
61
- Translate.plural = withContextFormatMessage((t) => t.plural);
62
- Translate.selectordinal = withContextFormatMessage((t) => t.selectordinal);
63
- Translate.custom = withContextFormatMessage((t) => t.custom);
64
-
65
- export const Intl = IntlContext.Consumer;
66
- export default Translate;
1
+ import React, { useContext } from 'react';
2
+ import IntlContext from '@jetshop/intl/context';
3
+ import formatMessage from 'format-message/types';
4
+
5
+ export const useIntl = () => useContext(IntlContext);
6
+
7
+ type FormatMsgType = typeof formatMessage;
8
+ type FormatMsgTypes =
9
+ | FormatMsgType
10
+ | FormatMsgType['rich']
11
+ | FormatMsgType['number']
12
+ | FormatMsgType['date']
13
+ | FormatMsgType['time']
14
+ | FormatMsgType['select']
15
+ | FormatMsgType['plural']
16
+ | FormatMsgType['selectordinal']
17
+ | FormatMsgType['custom'];
18
+
19
+ type WithContextFormatMessage = ReturnType<typeof withContextFormatMessage>;
20
+
21
+ interface TranslateType extends WithContextFormatMessage {
22
+ rich: (
23
+ ...args: Parameters<FormatMsgType['rich']>
24
+ ) => ReturnType<WithContextFormatMessage>;
25
+ number: (
26
+ ...args: Parameters<FormatMsgType['number']>
27
+ ) => ReturnType<WithContextFormatMessage>;
28
+ date: (
29
+ ...args: Parameters<FormatMsgType['date']>
30
+ ) => ReturnType<WithContextFormatMessage>;
31
+ time: (
32
+ ...args: Parameters<FormatMsgType['time']>
33
+ ) => ReturnType<WithContextFormatMessage>;
34
+ select: (
35
+ ...args: Parameters<FormatMsgType['select']>
36
+ ) => ReturnType<WithContextFormatMessage>;
37
+ plural: (
38
+ ...args: Parameters<FormatMsgType['plural']>
39
+ ) => ReturnType<WithContextFormatMessage>;
40
+ selectordinal: (
41
+ ...args: Parameters<FormatMsgType['selectordinal']>
42
+ ) => ReturnType<WithContextFormatMessage>;
43
+ custom: (
44
+ ...args: Parameters<FormatMsgType['custom']>
45
+ ) => ReturnType<WithContextFormatMessage>;
46
+ }
47
+
48
+ const withContextFormatMessage =
49
+ (callback: (t: FormatMsgType) => ReturnType<FormatMsgTypes>) =>
50
+ (...args: Parameters<FormatMsgTypes>) =>
51
+ React.createElement(IntlContext.Consumer, null, (t: FormatMsgType) =>
52
+ callback(t)(...args)
53
+ );
54
+
55
+ const Translate = withContextFormatMessage((t) => t) as TranslateType;
56
+ Translate.rich = withContextFormatMessage((t) => t.rich);
57
+ Translate.number = withContextFormatMessage((t) => t.number);
58
+ Translate.date = withContextFormatMessage((t) => t.date);
59
+ Translate.time = withContextFormatMessage((t) => t.time);
60
+ Translate.select = withContextFormatMessage((t) => t.select);
61
+ Translate.plural = withContextFormatMessage((t) => t.plural);
62
+ Translate.selectordinal = withContextFormatMessage((t) => t.selectordinal);
63
+ Translate.custom = withContextFormatMessage((t) => t.custom);
64
+
65
+ export const Intl = IntlContext.Consumer;
66
+ export default Translate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetshop/intl",
3
- "version": "5.13.2",
3
+ "version": "5.13.12-0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "bin": {
package/tooling.mdx CHANGED
@@ -1,33 +1,33 @@
1
- ---
2
- name: Tooling
3
- menu: '@jetshop/intl'
4
- route: /intl/tooling
5
- ---
6
-
7
- # Tooling
8
-
9
- The `@jetshop/intl` package also exposes some tooling to make translating your shop easier.
10
-
11
- ## Extracting translations
12
-
13
- Before starting to translate the shop, you need to extract the translation strings from the source code. `@jetshop/intl` provides the command `extract` that searches through your codebase and extracts all translation strings and puts them in `translations/default.json` in your shop.
14
- By default this script is added to `package.json` in your shop, so you can run this in a terminal:
15
-
16
- ```bash
17
- yarn intl extract
18
- ```
19
-
20
- Once the translation strings are extracted they get a unique ID, which is used to know where the translation string should be used. Based on the `default.json` file you can then create a file for every locale you want to support, e.g. `sv.json` (Swedish) and `en.json` (English).
21
- Usually the translation strings in the source code are written in English, so `en.json` should just be a copy of `default.json`.
22
-
23
- If you add a new locale, make sure to also update the `translations/index.js` file which is an entry point for all translations.
24
-
25
- When the app is rendered (on server and client) Flight will use the translation provided in your localized json file, and fall back to the default translation if it's not found.
26
-
27
- ## Linting translations
28
-
29
- In order to make sure your translations are complete, there's also a linting command. This checks all translation files in your `translations/` folder to make sure there's no missing translations anywhere. Run this command in your terminal:
30
-
31
- ```bash
32
- yarn intl lint
33
- ```
1
+ ---
2
+ name: Tooling
3
+ menu: '@jetshop/intl'
4
+ route: /intl/tooling
5
+ ---
6
+
7
+ # Tooling
8
+
9
+ The `@jetshop/intl` package also exposes some tooling to make translating your shop easier.
10
+
11
+ ## Extracting translations
12
+
13
+ Before starting to translate the shop, you need to extract the translation strings from the source code. `@jetshop/intl` provides the command `extract` that searches through your codebase and extracts all translation strings and puts them in `translations/default.json` in your shop.
14
+ By default this script is added to `package.json` in your shop, so you can run this in a terminal:
15
+
16
+ ```bash
17
+ yarn intl extract
18
+ ```
19
+
20
+ Once the translation strings are extracted they get a unique ID, which is used to know where the translation string should be used. Based on the `default.json` file you can then create a file for every locale you want to support, e.g. `sv.json` (Swedish) and `en.json` (English).
21
+ Usually the translation strings in the source code are written in English, so `en.json` should just be a copy of `default.json`.
22
+
23
+ If you add a new locale, make sure to also update the `translations/index.js` file which is an entry point for all translations.
24
+
25
+ When the app is rendered (on server and client) Flight will use the translation provided in your localized json file, and fall back to the default translation if it's not found.
26
+
27
+ ## Linting translations
28
+
29
+ In order to make sure your translations are complete, there's also a linting command. This checks all translation files in your `translations/` folder to make sure there's no missing translations anywhere. Run this command in your terminal:
30
+
31
+ ```bash
32
+ yarn intl lint
33
+ ```
package/tsconfig.json CHANGED
@@ -1,8 +1,8 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "rootDir": ".",
5
- "declaration": true,
6
- "allowJs": false
7
- }
8
- }
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "declaration": true,
6
+ "allowJs": false
7
+ }
8
+ }