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"