diff --git a/.circleci/config.yml b/.circleci/config.yml
index 90b38b4cd0..07dddc3d39 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,6 +11,10 @@ aliases:
name: Building locales
command: yarn build:i18n
+ - &build
+ name: Building project
+ command: yarn build:prod:npm
+
- &restore-yarn-cache
keys:
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
@@ -103,6 +107,7 @@ jobs:
- save-cache: *save-yarn-cache
- run: *clean
- run: *i18n
+ - run: *build
- run:
name: Chromatic
command: ./scripts/chromatic.sh
diff --git a/.gitignore b/.gitignore
index 22ed91efb6..cce8c85424 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ dist
es
i18n/*.js
i18n/json
+i18n/bundles
node_modules
npm-debug.log
npm-error.log
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
index e1211aa23d..d2f24da26d 100644
--- a/.storybook/preview.tsx
+++ b/.storybook/preview.tsx
@@ -1,5 +1,4 @@
-import * as React from "react";
-import { IntlProvider } from 'react-intl';
+import { boxLanguages } from '@box/languages';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import features from '../examples/src/features';
@@ -7,7 +6,7 @@ import features from '../examples/src/features';
import '../src/styles/variables';
import '../src/styles/base.scss';
-import { reactIntl } from "./reactIntl";
+import { reactIntl } from './reactIntl';
// Constants
global.FEATURE_FLAGS = global.FEATURE_FLAGS || features;
@@ -19,14 +18,7 @@ global.TOKEN = global.TOKEN || 'P1n3ID8nYMxHRWvenDatQ9k6JKzWzYrz';
initialize();
const preview = {
- decorators:[
- mswDecorator,
- (Story) => (
-
-
-
- )
- ],
+ decorators: [mswDecorator],
parameters: {
chromatic: { disableSnapshot: true },
@@ -48,11 +40,11 @@ const preview = {
initialGlobals: {
locale: reactIntl.defaultLocale,
- locales: {
- en: 'English',
- de: 'Deutsche',
- },
- }
+ locales: Object.keys(boxLanguages).reduce((acc, key) => {
+ acc[key] = boxLanguages[key].name;
+ return acc;
+ }, {}),
+ },
};
export default preview;
diff --git a/.storybook/reactIntl.ts b/.storybook/reactIntl.ts
index fa57a63878..496430622e 100644
--- a/.storybook/reactIntl.ts
+++ b/.storybook/reactIntl.ts
@@ -1,21 +1,16 @@
-import enUS from '../i18n/en-US';
-import deDE from '../i18n/de-DE';
-import frFR from '../i18n/fr-FR';
-import jaJP from '../i18n/ja-JP';
-import zhCN from '../i18n/zh-CN';
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const boxLanguages = require('@box/languages');
-const locales = ['en', 'de', 'fr', 'jp', 'zh'];
-
-const messages = {
- 'en': { ...enUS },
- 'de': { ...deDE },
- 'fr': { ...frFR },
- 'jp': { ...jaJP },
- 'zh': { ...zhCN },
-};
+const messages = boxLanguages.reduce(
+ (acc, lang) => ({
+ ...acc,
+ // eslint-disable-next-line global-require,import/no-dynamic-require,@typescript-eslint/no-var-requires
+ [lang]: require(`../i18n/bundles/${lang}`).messages, // whatever the relative path to your messages json is
+ }),
+ {},
+);
export const reactIntl = {
- defaultLocale: 'en',
- locales,
+ defaultLocale: 'en-US',
messages,
};
diff --git a/i18n.config.js b/i18n.config.js
new file mode 100644
index 0000000000..9c1e52a46a
--- /dev/null
+++ b/i18n.config.js
@@ -0,0 +1,4 @@
+module.exports = {
+ // packages to bundle into translation files
+ translationDependencies: ['@box/box-ai-content-answers'],
+};
diff --git a/package.json b/package.json
index 58312f073e..4272a5a833 100644
--- a/package.json
+++ b/package.json
@@ -124,6 +124,7 @@
"@babel/types": "^7.24.7",
"@box/blueprint-web": "^7.19.8",
"@box/blueprint-web-assets": "^4.21.0",
+ "@box/box-ai-content-answers": "^0.49.1",
"@box/cldr-data": "^34.2.0",
"@box/frontend": "^10.0.0",
"@box/languages": "^1.0.0",
@@ -279,7 +280,7 @@
"source-map-loader": "^0.2.4",
"start-server-and-test": "^1.10.8",
"storybook": "^8.2.4",
- "storybook-react-intl": "^3.0.1",
+ "storybook-react-intl": "^3.1.1",
"string-replace-loader": "^3.1.0",
"styled-components": "5.0.0",
"stylelint": "^12.0.0",
@@ -300,6 +301,7 @@
"peerDependencies": {
"@box/blueprint-web": "^7.19.8",
"@box/blueprint-web-assets": "^4.21.0",
+ "@box/box-ai-content-answers": "^0.49.1",
"@box/cldr-data": ">=34.2.0",
"@box/react-virtualized": "9.22.3-rc-box.9",
"@hapi/address": "^2.1.4",
diff --git a/scripts/webpack.config.js b/scripts/webpack.config.js
index be7040bbef..ef820349aa 100644
--- a/scripts/webpack.config.js
+++ b/scripts/webpack.config.js
@@ -7,6 +7,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const RsyncPlugin = require('@box/frontend/webpack/RsyncPlugin');
+const { translationDependencies } = require('../i18n.config');
const packageJSON = require('../package.json');
const rsyncConf = fs.existsSync('scripts/rsync.json') ? require('./rsync.json') : {}; // eslint-disable-line
const license = require('./license');
@@ -27,7 +28,10 @@ const fileId = process.env.FILEID; // used for examples only
const outputDir = process.env.OUTPUT;
const version = isRelease ? packageJSON.version : 'dev';
const outputPath = outputDir ? path.resolve(outputDir) : path.resolve('dist', version, language);
-const Translations = new TranslationsPlugin();
+const Translations = new TranslationsPlugin({
+ generateBundles: true,
+ additionalMessageData: translationDependencies.map(pkg => `${pkg}/i18n/[language]`),
+});
const entries = {
picker: path.resolve('src/elements/wrappers/ContentPickers.js'),
uploader: path.resolve('src/elements/wrappers/ContentUploader.js'),
@@ -145,10 +149,10 @@ function getConfig(isReactExternalized) {
stats,
};
+ config.plugins.push(Translations);
+
if (isDev) {
config.devtool = 'source-map';
- config.plugins.push(Translations);
-
if (!examples) {
config.plugins.push(
new CircularDependencyPlugin({
diff --git a/src/elements/README.md b/src/elements/README.md
index 67812fc819..0cd6256533 100644
--- a/src/elements/README.md
+++ b/src/elements/README.md
@@ -8,13 +8,13 @@ The UI Elements use [react-intl](https://formatjs.io/docs/react-intl/) for inter
The `language` property is a string that can be one of `en-AU`, `en-CA`, `en-GB`, `en-US`, `bn-IN`, `da-DK`, `de-DE`, `es-419`, `es-ES`, `fi-FI`, `fr-CA`, `fr-FR`, `hi-IN`, `it-IT`, `ja-JP`, `ko-KR`, `nb-NO`, `nl-NL`, `pl-PL`, `pt-BR`, `ru-RU`, `sv-SE`, `tr-TR`, `zh-CN`, `zh-TW`.
-The `messages` property is a map of message keys and translated strings. All the messages that the UI elements use can be found under the [i18n](https://github.com/box/box-ui-elements/tree/master/i18n) folder. We distribute them as JS modules within the `box-ui-elements` npm package and they can be imported like any other module - `import box-ui-elements/i18n/[LANGUAGE FROM ABOVE]`. The code examples for each of the UI Elements assume `en-US` and show how the US english messages are imported in.
+The `messages` property is a map of message keys and translated strings. All the messages that the UI elements use can be found under the [i18n](https://github.com/box/box-ui-elements/tree/master/i18n) folder. We distribute them as JS modules within the `box-ui-elements` npm package and they can be imported like any other module - `import box-ui-elements/i18n/bundles/[LANGUAGE FROM ABOVE]`. The code examples for each of the UI Elements assume `en-US` and show how the US english messages are imported in.
If you are using the CDNs, the i18n messages are included in the bundle.
Example of using the `language` and `messages` properties:
```jsx static
-import messages from 'box-ui-elements/i18n/ja-JP';
+import messages from 'box-ui-elements/i18n/bundles/ja-JP';
```
@@ -23,7 +23,7 @@ _ContentExplorer will show in Japanese._
Example `IntlProvider` with multiple elements
```jsx static
-import messages from 'box-ui-elements/i18n/ja-JP';
+import messages from 'box-ui-elements/i18n/bundles/ja-JP';
@@ -36,8 +36,8 @@ _Both ContentExplorer and ContentPicker will show in Japanese._
Example of using different `language` and `messages` properties:
```jsx static
-import jaMessages from 'box-ui-elements/i18n/ja-JP';
-import deMessages from 'box-ui-elements/i18n/de-DE';
+import jaMessages from 'box-ui-elements/i18n/bundles/ja-JP';
+import deMessages from 'box-ui-elements/i18n/bundles/de-DE';
diff --git a/yarn.lock b/yarn.lock
index 33660a6ad1..f8a0383010 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1540,6 +1540,11 @@
tabbable "^4.0.0"
type-fest "^3.2.0"
+"@box/box-ai-content-answers@^0.49.1":
+ version "0.49.1"
+ resolved "https://registry.yarnpkg.com/@box/box-ai-content-answers/-/box-ai-content-answers-0.49.1.tgz#e5db564e68971793c629f00f469cfbd92d8a2bbb"
+ integrity sha512-TbZEU1QhlCU3p+iQdgStY5LQ7mzORt4bExHfCWRQpjI+8DdW4apLxo1gZOsQ5E7XWokg+mGbhl+ff3fkXG1Fhw==
+
"@box/cldr-data@^34.2.0":
version "34.8.0"
resolved "https://registry.yarnpkg.com/@box/cldr-data/-/cldr-data-34.8.0.tgz#36e6ddcea8e20653326aba2e0d13e07f34b7704f"
@@ -22285,17 +22290,17 @@ store2@^2.14.2:
resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.3.tgz#24077d7ba110711864e4f691d2af941ec533deb5"
integrity sha512-4QcZ+yx7nzEFiV4BMLnr/pRa5HYzNITX2ri0Zh6sT9EyQHbBHacC6YigllUPU9X3D0f/22QCgfokpKs52YRrUg==
-storybook-i18n@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/storybook-i18n/-/storybook-i18n-3.0.1.tgz#9320f6e091deb006c6db2e4ce632cccff8429047"
- integrity sha512-euCfw6ABqq8jSZ4fCaAIsNwk1FBIrSIr4kMP900bjUgO/mGgzZzXmrYmZaXs51mx9A2p87xo0OktPPZUuKYCTQ==
+storybook-i18n@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/storybook-i18n/-/storybook-i18n-3.1.1.tgz#d75f84d62682a76d58586534719a7f5042b82cb9"
+ integrity sha512-k1/lS+Rx6l5mJEYAHQWEgXuwU5IyWk7kjcJtm2FDIn1UqZwbEraGlrp/fEZKK2e/7+SXEQdKeCaTz7+63WTQxw==
-storybook-react-intl@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/storybook-react-intl/-/storybook-react-intl-3.0.1.tgz#29187f481e1996f858c9b55e3017e10879e5e468"
- integrity sha512-ZKZibA3F17+dfSDQ7UpGNDwO0OMcakR5nZRGMVF+egmdtb/ADkEb7E1orQgXD/i57xxWDkL0IemRoI1I7rA1Kw==
+storybook-react-intl@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/storybook-react-intl/-/storybook-react-intl-3.1.1.tgz#48642816c6733d977c1a7fc285504bd49372ba79"
+ integrity sha512-0s0GshbSnNcg8DUM0LHY1C5NxUHFc4aLWqssBSquhV3I8gVqxuyFVdOuMkuUJWtqeJbN1audXjBAR+31N+NMow==
dependencies:
- storybook-i18n "^3.0.1"
+ storybook-i18n "3.1.1"
storybook@^8.2.4:
version "8.2.4"
@@ -22408,7 +22413,8 @@ string-replace-loader@^3.1.0:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
-"string-width-cjs@npm:string-width@^4.2.0":
+"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+ name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -22426,15 +22432,6 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
-"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@@ -22573,7 +22570,8 @@ stringify-package@^1.0.0, stringify-package@^1.0.1:
resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85"
integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ name strip-ansi-cjs
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -22601,13 +22599,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -24807,7 +24798,8 @@ worker-farm@^1.6.0, worker-farm@^1.7.0:
dependencies:
errno "~0.1.7"
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+ name wrap-ansi-cjs
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -24850,15 +24842,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"