{"_id":"@alicloud/xconsole-intl-extended","name":"@alicloud/xconsole-intl-extended","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@alicloud/xconsole-intl-extended","version":"1.0.0","description":"强化 Intl，以最佳实践和规范，统一样式，减少代码量","sideEffects":false,"license":"MIT","main":"build/cjs/index.js","module":"build/esm/index.js","types":"build/types/index.d.ts","author":{"name":"jianchun.wjc","email":"jianchun.wjc@alibaba-inc.com"},"publishConfig":{"access":"public"},"keywords":[],"devDependencies":{"@alicloud/console-toolkit-cli":"^1.2.30","@alicloud/console-toolkit-preset-component":"^1.2.61","@alicloud/console-base-demo-helper-top-nav":"^1.1.8","@alicloud/demo-rc-elements":"^1.11.15","@alicloud/ts-config":"^1.1.2","@types/lodash-es":"^4.17.7","@types/react":"^17.0.48","@types/styled-components":"^5.1.26","react":"^17.0.2","styled-components":"^5.3.9","typescript":"^4.9.5"},"peerDependencies":{"react":">=16.8","styled-components":">=5"},"dependencies":{"@alicloud/console-base-rc-intl":"^1.4.9","@alicloud/console-components-intl":"2.0.1","@alicloud/mere-dom":"^1.6.10","lodash-es":"^4.17.21"},"gitHead":"b5e41ce23c3cdfc3b0cbfeb57fac1e3c736a43bd","scripts":{"start":"breezr start-storybook","test":"breezr test:unit","clean":"rm -rf build","build":"npm run build:esm && npm run build:cjs && npm run build:bundle && npm run build:typings","build:esm":"breezr build --engine babel --es-module","build:cjs":"breezr build --engine babel","build:bundle":"breezr build --engine webpack","build:typings":"tsc --outDir build/types --declaration --emitDeclarationOnly"},"_id":"@alicloud/xconsole-intl-extended@1.0.0","_integrity":"sha512-Lc7xPl7hwFsk+GVKU4rPdeXdowpCN07w9sSIz7UQu8v4ZH3Vu07qiZn45AehZZp2+lrnsE5mmK/Y167oLYffWA==","_resolved":"/private/var/folders/fh/fjnt47r94lz_ctjrvv4cwh3c0000gp/T/ed9d2de24901fbb2891010fd6807bfd3/alicloud-xconsole-intl-extended-1.0.0.tgz","_from":"file:alicloud-xconsole-intl-extended-1.0.0.tgz","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-Lc7xPl7hwFsk+GVKU4rPdeXdowpCN07w9sSIz7UQu8v4ZH3Vu07qiZn45AehZZp2+lrnsE5mmK/Y167oLYffWA==","shasum":"36c19c2bc7dfb116aed3ae5a78434b54acc51fce","tarball":"https://registry.npmjs.org/@alicloud/xconsole-intl-extended/-/xconsole-intl-extended-1.0.0.tgz","fileCount":56,"unpackedSize":484547,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHnK6umGjpsJLmZwd7TIkKgLZne02DGwzxXTM/pwrfxoAiB7zujgXP156Y6UR3niQSw6DLw+Nrjw2HmPuNqZhg5rAQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkHWYCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrN/g/8DWfbZDZF5j6zlfUzr3Q8BJWBfPkABz4CNdVPPPRFEEawrGJt\r\nPmrfLDJk2FwIMAITRGJNFsEJ2P7Qk63OvrXUGY8pUYYbVopa5iug5m0Zt+X8\r\nub97m1nQwZqMt2pvedZLBOMXITD2mGl4rzGHTTqyRIclzcqKVZoIE63GWLgv\r\n+8XovGY+tcWY31XxSlnzJnZ0yNVUH8XrhcsQb471U6HkVAFxNtb/swYf4wnx\r\naaTWGYEoHG7oPSfkehQYvfkwjuYROxqAwCiJYGgkNq96PPsbdA4mz+r/U7s1\r\nDe6x6w+Sh3bqiYKvP7hi8raa9DZNBIswVOdrP+SiClt7rnQz/iKploYnT4de\r\nXs7Cjd0uDY+2x3qt/Tg6Sb1PDD3KGfv9jq75D52Oy+e495AJhFgcIE7ygEJf\r\nRrbu/lm7gl7lGH4JHPhNuKZttMMWtgyqDnDGCkej5AI9l0Bc1BV5DsauIxd2\r\nIW/KY4IVPhwafFNZYveC8KSwiGTnIFD6VOMHf0oEijR4CCJprj4XdmnLJsOu\r\nRja4C0ETOTCtyOjb8gQ1tFvEitc1bg0N1on++g9Evv7LISFHDqbtG70tiOF3\r\nzRYgDnZ+Jcu7RtYYMSG9P/rOtjsEJcd6/+g2bqxVuTPwpbejbdV5z/Lwgff9\r\nknOX6YHlQNUOFXaDTa41IV0tJfw508wyf3k=\r\n=ml7/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"console-fe","email":"console-fe@alibabacloud.com"},"directories":{},"maintainers":[{"name":"jacksontian","email":"shyvo1987@gmail.com"},{"name":"fengmk2","email":"fengmk2@gmail.com"},{"name":"pagecao","email":"cpj1106@gmail.com"},{"name":"aliyunsdkteam","email":"sdk-team@alibabacloud.com"},{"name":"console-fe","email":"console-fe@alibabacloud.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/xconsole-intl-extended_1.0.0_1679648258679_0.7744600662753482"},"_hasShrinkwrap":false}},"time":{"created":"2023-03-24T08:57:38.600Z","1.0.0":"2023-03-24T08:57:38.841Z","modified":"2023-03-24T08:57:39.131Z"},"maintainers":[{"name":"jacksontian","email":"shyvo1987@gmail.com"},{"name":"fengmk2","email":"fengmk2@gmail.com"},{"name":"pagecao","email":"cpj1106@gmail.com"},{"name":"aliyunsdkteam","email":"sdk-team@alibabacloud.com"},{"name":"console-fe","email":"console-fe@alibabacloud.com"}],"description":"强化 Intl，以最佳实践和规范，统一样式，减少代码量","keywords":[],"author":{"name":"jianchun.wjc","email":"jianchun.wjc@alibaba-inc.com"},"license":"MIT","readme":"# @alicloud/xconsole-intl-extended\n\n对 `@alicloud/console-components-intl` 的封装，使其更易用。\n\n> 该版本依赖 `@alicloud/console-components-intl` 版本 `2.x`，如果是 `1.x` 可以使用 `@ali/wind-x-intl`。\n\n## 吐槽 `@alicloud/console-components-intl`\n\n| 问题                                                | 改进                                                                       |\n|---------------------------------------------------|--------------------------------------------------------------------------|\n| 对 `@alicloud/console-components-intl` 的使用有加载顺序的要求 | 不需要                                                                      |\n| 没有样式规范                                            | 定义了一套方便用户的规则，可以按照 `key` 的后缀指令选择是返回字符串还是带标准样式的 JSX，既保证样式标准性，又可以避免断句、断章等问题 |\n| 没有类型保护，编译期无法检测到某个 `key` 不存在，从而导致线上问题              | 通过 TS 泛型，既保证代码的完整性，又能够避免线上问题                                             |\n| 很难单独使用在 npm 包中                                    | 因为是工厂方法，可以创建多个实例                                                         |\n| 没有对 locale 的 key 做兼容                              | 对大小和连字符等做了较强的兼容                                                          |\n\n## 安装\n\n```shell\ntnpm i -S react @alicloud/console-components @alicloud/console-components-intl styled-components @alicloud/xconsole-intl-extended\n```\n\n`@alicloud/xconsole-intl-extended` 之前的是它的 `peer dependency`，必须由使用者安装。\n\n## 设置\n\n> 首先请非常冷酷无情地删掉狗屎的 `initializer.js`。\n\n在你项目的 `src` 下建一个目录，并创建如下文件（也可以按照你的个人喜好）：\n\n```text\nsrc/\n └─ intl/\n     ├─ locales/\n     │   ├─ en-us.json\n     │   ├─ ...\n     │   └─ zh-cn.json\n     ├─ index.ts\n     └─ messages.ts\n```\n\n## `intl/`\n\n建议在你的 webpack 配置下，为 `src` 目录配置 alias `:`（个人习惯，Xconsole 下是 `~`），比如在 `webpack.config.base.json` 里：\n\n```javascript\nconst path = require('path');\n// ...\n\nmodule.exports = {\n  // ...\n  resolve: {\n    alias: {\n      ':': path.resolve(__dirname, '../src')\n    }\n    // ...\n  }\n  // ...\n}\n```\n\n如果你用 TS，修改 `tsconfig.json`：\n\n```json\n{\n  \"compilerOptions\": {\n    // ...\n    \"paths\": {\n      \":/*\": [\"src/*\"]\n    }\n    // ...\n  }\n}\n```\n\n### `intl/locales/[locale].json`\n\n`locales` 下只是对 [美杜莎](http://mcms-portal.alibaba-inc.com) 上的一个导出和备份，主要是为了在本地仓库有个参考，可以快速定位到正确的 key。\n\n### `intl/message.ts`\n\n```js\nimport fallbackMessages from './locales/en-us.json'; // locales 下的 json 文件最多就是个参考，但还是会打一个到包里做兜底\n\nexport default {\n  ...fallbackMessages,\n  // ↓ 开发期间「增加」新的文案，发布前必须提交到 medusa，并删除之间的文案 - BEGIN\n  // ↑ 开发期间「增加」新的文案，发布前必须提交到 medusa，并删除之间的文案 - END\n  ...window.SOME_GLOBAL_MESSAGES // window 或哪里可以直接拿到的 medusa 输出的对象\n  // ↓ 开发期间「修改」已有的文案，发布前必须提交到 medusa，并删除之间的文案 - BEGIN\n  // ↑ 开发期间「修改」已有的文案，发布前必须提交到 medusa，并删除之间的文案 - END\n};\n```\n\n注意：`SOME_GLOBAL_MESSAGES` 视你的具体应用而定。\n\n### `intl/index.ts`\n\n```typescript\nimport createIntl from '@alicloud/xconsole-intl-extended';\n\nimport messages from './messages';\n\nconst intl = createIntl(messages, LOCALE, { // LOCALE 视你应用的具体情况而定，你也可以通过 options 做一些定制\n  extraValues\n});\n\nexport default intl;\n\nexport const { // 你也可以不必输出它们，而直接使用 intl.xx 进行引用\n  getLocale,\n  intlDate,\n  intlNumber,\n  intlPercentage,\n  intlPercentageTuple,\n  intlByte,\n  intlByteTuple,\n  intlCurrency,\n  intlConst,\n  intlChoices\n} = intl;\n```\n\n## 如何使用\n\n记住：在你的项目里，你永远只跟 `:/intl` 打交道，绝对不要染指 `@alicloud/console-components-intl`。\n\n```jsx\nimport intl, {\n  // other stuff\n} from ':/intl';\n\n// 静态调用\nintl(...);\n\n// 在 JSX 中\n<div>{intl(...)}</div>\n```\n\n## 关于返回类型\n\n`intl` 方法虽然返回的类型只有 `string`，但实际可能返回 `JSX.Element`（在指定 `!lines` 或 `!html` 的情况下），这个非常不好写，又不适合用泛型（不知怎么回事会返回 `any`）。\n\n好在大多数情况下，`intl` 仅用于展示端，`string` 或 `JSX.Element` 可以无感。\n\n如果要强行指定 `JSX.Element` 的话，就强行转换类型吧：\n\n```typescript\nconst message = intl('xxx!html') as unknown as JSX.Element;\n```\n\n## 国际化规范（最佳实践）\n\n### 关于 key\n\n* 全小写，单词之间以 `_` 分隔\n* 建议以 `:` 做分隔（`.` 也行），该分隔符一般\n* 一般是 `对象:类型约定:含义?参数!长相`\n  - **对象**：表示功能对象，但在某些包下面，有明确的上下文的时候可以不需要\n  - **类型约定**：文案的类型\n  - **含义**：表示文案的意思，需要让读者看到它就知道大致的意思，而不需要进一步看文案的内容，如果有插值则以 `{val1,val2}` 的形式挂上\n  - **参数**：如果内部有 `select` 语句则带上参数，如 `?attr`、`?op`、`?status`、`?type`\n  - **长相**：用 `!html` 表示内容里边有 HTML 元素，`!lines` 表示内容里边的换行需要解析成 `<p>`、`<ul>`、`<ol>`\n* 尽可能聚合，如用户的属性 `user?attr`，然后内部使用 `select`\n* 通用的文案以 `_` 打头，如 `_?op`\n\n### 关于 value\n\n* 不断句，即不能有 `我有` + `个女朋友` 两条，而是应该用插值 `我有 {n, number} 个女朋友`\n* 不断章，相关联的文案放一个 key 里，不可一行文案对一个 key，见过 `xx1`、`xx2`... 然后在代码里把它们拼成一个段落（可能是列表）的行为，恶心至极\n* 注意标点，中文下不允许有英文标点，英文下不允许有中文标点\n* 英文、数字、HTML inline 元素，若和中文贴着，两者之间加空格\n* 仅允许 `<a>`、`<em>` 、`<code>`、`<strong>`、`<kbd>`这几个 inline 元素\n* `<ul>` 的 `<li>` 以 `*␣` 打头（注意有空格）\n* `<ol>` 的 `<li>` 以 `1.␣`、`2.␣`...打头（注意有空格）\n* 用作例子的部分，以 `<code>` 包裹\n\n### 关于 key 的「类型约定」建议\n\n> 我发现很多人对「类型」一点都不感冒，什么是「属性」，什么是「操作」一点都没有概念，导致写出来的文案虽然在用户看来没有问题，但定义的却一塌糊涂。\n\n一般我会用 **一个** 单词来表示文案的类型，每种类型在含义、书写上都会有一定的特点，在一个项目下，不可能有太多的类型，以下是我在项目中常用的类型：\n\n| 单词        | 简写  | 含义  | 场景                                 | 文案规范                         |\n|-----------|-----|-----|------------------------------------|------------------------------|\n| `attr`    | `a` | 属性  | 标题      | 名词，英文遵循首每个单词（除介词、冠词等）字母大写    |\n| `op`      | `o` | 属性  | 按钮      | 动词，英文遵循首每个单词（除介词、冠词等）字母大写    |\n| `type`    | `t` | 类型  | 字段展示    | 名词，英文遵循首每个单词（除介词、冠词等）字母大写    |\n| `status`  | `s` | 状态  | 字段展示    | 形容词，英文遵循首每个单词（除介词、冠词等）字母大写   |\n| `title`   | -   | 标题  | 标题      | 名词，英文遵循首每个单词（除介词、冠词等）字母大写    |\n| `label`   | `l` | 标题  | 按钮、字段展示 | 名词/动词，英文遵循首每个单词（除介词、冠词等）字母大写 |\n| `message` | `m` | 长句子 | 标题  | 必须有结束标点，不可断句，不可断章            |\n| `phrase`  | `p` | 短句子 | 标题  | 没有结束标点的句子（不建议）               |\n\n以上，我们可以将在特定对象的 `attr`、`op`、`type`、`status` 进行聚合，比如：\n\n```typescript\nexport default {\n  'user:attr:id': '用户 ID',\n  'user:attr:name': '用户名',\n  'user:op:create': '创建用户',\n  'user:op:edit': '编辑用户',\n  'user:op:delete': '删除用户',\n  'user:type:normal': '普通',\n  'user:type:vip': 'VIP',\n  'user:type:svip': '超级 VIP',\n  'user:status:new': '未认证',\n  'user:status:verified': '已认证',\n  'user:status:unregstered': '已注销',\n};\n```\n\n聚合后，可以是这样：\n\n```typescript\nexport default {\n  'user?attr': `{attr, select,\nID {用户 ID}\nNAME {用户名}\nother {{attr}}}`,\n  'user?op': `{op, select,\nCREATE {创建用户}\nEDIT {编辑用户}\nDELETE {删除用户}\nother {{op}}}`,\n  'user?type': `{type, select,\nNORMAL {普通}\nVIP {VIP}\nSVIP {超级 VIP}\nother {{type}}}`,\n  'user?status': `{status, select,\nNEW {未认证}\nVERIFIED {已认证}\nUNREGSTERED {已注销}\nother {{status}}}`\n};\n```\n\n我更推荐这种聚合的定义方式，它有以下好处：\n\n1. 更内聚，相关的内容一定会定义在一个地方\n2. 可以用 `intlChoices` 搭配常量枚举快速生成选项列表\n3. 可以用 `intlConst` 搭配常量枚举快速转化常量为文案\n\n## FAQ\n\n### 可以直接用 `@alicloud/console-components-intl` 么？\n\n可以，但一万个不推荐。你可以在你的 `intl/index.ts` 的最末把 `@alicloud/console-components-intl` export 出去再用，但千万不要直接用 `@alicloud/console-components-intl`，\n原因是你在别人 import 的 `@alicloud/console-components-intl` 有可能是没有初始化完毕的——这就是那个臭烘烘的 `initializer.js` 的来由。\n\n### 文案中可否有 HTML？\n\n可以，首先你的 key 在末尾要带上 HTML 指令 `!html`（你可以自定义）。\n\n其次，不要有 block 级别的元素，可以用 `strong`、`em`、`code`、`kbd`、`a`，这些元素会有一定的样式。\n\n### 如何转义花括号？\n\n`This '{isn''t}' obvious` → `This {isn't} obvious`\n\n参考 <https://unicode-org.github.io/icu/userguide/format_parse/messages/#quotingescaping>\n\n### 文案如何不断章？\n\n不断句和不断章，这个在国际化的场景下非常重要。不断句可以利用插值，那么不断章（即一个文案中可能有多个段落）怎么处理呢？\n\n你的 key 需要有 `!lines` 指令（同样可以自定义），你写文案的时候，该文案是带换行（`\\n`）的。\n\n文案中的每一行，会被解析成 `p`，`ul > li`（该行以 `1.␣`、`2.␣` 等打头）或 `ol > li`（该行以 `*␣` 打头）。\n\n`!html` 和 `!lines` 是可以合起来用的，比如 `!html!lines` 或 `!lines!html`（建议选择一种风格，不要都用）。\n\n### 文案中的链接怎么跟渠道链接配置结合？\n\n你只需要在调用工厂方法的时候，第三个 `options` 参数中传入 `extraValues` 即可：\n\n```typescript\nwindXIntl(messages, LOCALE, { // LOCALE 视你应用的具体情况而定，你也可以通过 options 做一些定制\n  extraValues: LINKS\n});\n```\n\n以上，`LINKS` 是你的应用在 viper 配置的渠道链接的输出。\n\n而你在定义你的文案时，这么定义：\n\n```json\n{\n  \"x:message:about_xx_with_some_link!html\": \"...，详情可以查看 <a href=\\\"{help:xxx}\\\" target=\\\"_blank\\\">帮助文档</a>\"\n}\n```\n\n注意 `help:xxx` 不可定义成 `help.xxx`，会报错（至少我当时用的时候是这样）。\n\n于是，当你 `intl('x:message:about_xx_with_some_link!html')` 的时候，其中的链接自然而然地被替换成了渠道链接中对应的配置。\n\n但是，如果渠道链接是需要参数的，就无法这么做了。\n\n### 文案中有 HTML 和换行，但 key 末尾没有指令，如何正常展示？\n\n首先，强烈推荐 key 末尾带上 `!html` 和 `!lines` 指令，它的好处在于看到 key 就知到它长什么样，而且你不需要额外的编码就可以自动展示成你期望的样式。\n\n但如果你一定不加，你可以用 `intl` 的第三个参数，`html` 和 `lines` 可以同时存在，也可以只有一个为 `true`：\n\n```typescript\nintl('x:message:with_html_but_no_indicators', undefined, {\n  html: true,\n  lines: true\n})\n```\n\n### 如何避免传错 key？\n\n你必须本地有一份「完整」的兜底文案，这很重要，一是保证代码的完整性，二是，在使用 TS 的场景下，可以帮你避免输错 key 的场景。\n","readmeFilename":"README.md"}