@red-hat-developer-hub/backstage-plugin-translations 0.3.1 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-translations
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 71cd222: Backstage version bump to v1.54.6
8
+
9
+ ## 0.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 838a80d: Add NFS (New Frontend System) support and graduate to stable. NFS extensions include PageBlueprint, ApiBlueprint, TranslationBlueprint, and AppRootWrapperBlueprint. OFS exports are now available at `./legacy`.
14
+ - 6456930: Add pseudo-localization support using `i18next-pseudo` to help identify untranslated or hardcoded strings in the UI. Strings are transformed with diacritical marks and brackets (e.g., `"Settings"` → `"[Ṣḛḛţţḭḭṇḡṡ]"`). Activate via `?pseudolocalization=true` URL parameter or `i18n.pseudolocalization.enabled: true` in app-config. Includes a `PseudoLocalizationProvider` component for use as a dynamic plugin at the `application/provider` mount point.
15
+ - 772a1a6: Backstage version bump to v1.52.1
16
+
3
17
  ## 0.3.1
4
18
 
5
19
  ### Patch Changes
@@ -0,0 +1,16 @@
1
+ # please keep this in sync with rhdh/dynamic-plugins.default.yaml
2
+ dynamicPlugins:
3
+ frontend:
4
+ red-hat-developer-hub.backstage-plugin-translations:
5
+ translationResources:
6
+ - importName: translationsPluginTranslations
7
+ module: Alpha
8
+ ref: translationsPluginTranslationRef
9
+ dynamicRoutes:
10
+ - path: /translations
11
+ importName: TranslationsPage
12
+ module: Legacy
13
+ mountPoints:
14
+ - mountPoint: application/provider
15
+ importName: PseudoLocalizationProvider
16
+ module: Legacy
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "i18n": {
6
+ "type": "object",
7
+ "properties": {
8
+ "pseudolocalization": {
9
+ "type": "object",
10
+ "properties": {
11
+ "enabled": {
12
+ "type": "boolean",
13
+ "description": "When true, enables i18next-pseudo for strings that go through the translation API.",
14
+ "visibility": "frontend"
15
+ },
16
+ "language": {
17
+ "type": "string",
18
+ "description": "Language code to pseudolocalize (e.g. en, de). Defaults to the active UI language when omitted.",
19
+ "visibility": "frontend"
20
+ }
21
+ },
22
+ "description": "Pseudolocalization stretches translated strings to test layout (i18next-pseudo).",
23
+ "deepVisibility": "frontend"
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
package/dist/alpha.d.ts CHANGED
@@ -1,17 +1,16 @@
1
1
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
2
2
 
3
3
  /**
4
- * @alpha
4
+ * @public
5
5
  */
6
6
  declare const translationsPluginTranslationRef: _backstage_frontend_plugin_api.TranslationRef<"plugin.translations", {
7
+ readonly "page.title": string;
8
+ readonly "page.subtitle": string;
7
9
  readonly "table.title": string;
8
10
  readonly "table.headers.refId": string;
9
11
  readonly "table.headers.key": string;
10
12
  readonly "table.options.pageSize": string;
11
13
  readonly "table.options.pageSizeOptions": string;
12
- readonly "language.displayFormat": string;
13
- readonly "page.title": string;
14
- readonly "page.subtitle": string;
15
14
  readonly "export.title": string;
16
15
  readonly "export.downloadButton": string;
17
16
  readonly "export.filename": string;
@@ -19,11 +18,11 @@ declare const translationsPluginTranslationRef: _backstage_frontend_plugin_api.T
19
18
  readonly "common.error": string;
20
19
  readonly "common.noData": string;
21
20
  readonly "common.refresh": string;
21
+ readonly "language.displayFormat": string;
22
22
  }>;
23
23
 
24
24
  /**
25
- * @alpha
26
- *
25
+ * @public
27
26
  */
28
27
  declare const translationsPluginTranslations: _backstage_frontend_plugin_api.TranslationResource<"plugin.translations">;
29
28
 
@@ -0,0 +1,48 @@
1
+ import Pseudo from 'i18next-pseudo';
2
+
3
+ function mergePostProcess(i18n) {
4
+ const cur = i18n.options.postProcess;
5
+ let list;
6
+ if (!cur) {
7
+ list = [];
8
+ } else if (Array.isArray(cur)) {
9
+ list = [...cur];
10
+ } else {
11
+ list = [cur];
12
+ }
13
+ if (!list.includes("pseudo")) {
14
+ list.push("pseudo");
15
+ }
16
+ i18n.options.postProcess = list;
17
+ }
18
+ function attachPseudolocalization(translationApi, options) {
19
+ const i18n = translationApi.getI18nInstance();
20
+ const wrapped = options?.wrapped ?? true;
21
+ const languageToPseudo = options?.languageToPseudo ?? i18n.resolvedLanguage ?? i18n.language ?? "en";
22
+ const pseudo = new Pseudo({
23
+ enabled: true,
24
+ wrapped,
25
+ languageToPseudo
26
+ });
27
+ i18n.use(pseudo);
28
+ mergePostProcess(i18n);
29
+ const syncLanguageToPseudo = () => {
30
+ const next = options?.languageToPseudo ?? i18n.resolvedLanguage ?? i18n.language ?? "en";
31
+ pseudo.configurePseudo({ languageToPseudo: next });
32
+ };
33
+ syncLanguageToPseudo();
34
+ i18n.on("languageChanged", syncLanguageToPseudo);
35
+ }
36
+ function attachPseudolocalizationIfEnabled(translationApi, configApi, locationSearch) {
37
+ const params = new URLSearchParams(locationSearch ?? window.location.search);
38
+ const fromQuery = params.get("pseudolocalization") === "true";
39
+ const fromConfig = configApi.getOptionalBoolean("i18n.pseudolocalization.enabled") ?? false;
40
+ if (!fromQuery && !fromConfig) {
41
+ return;
42
+ }
43
+ const languageToPseudo = params.get("lng") ?? configApi.getOptionalString("i18n.pseudolocalization.language") ?? void 0;
44
+ attachPseudolocalization(translationApi, { languageToPseudo });
45
+ }
46
+
47
+ export { attachPseudolocalization, attachPseudolocalizationIfEnabled };
48
+ //# sourceMappingURL=pseudolocalization.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pseudolocalization.esm.js","sources":["../../src/apis/pseudolocalization.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ConfigApi } from '@backstage/core-plugin-api';\nimport type { i18n as I18n } from 'i18next';\nimport Pseudo from 'i18next-pseudo';\n\nimport type { I18nextTranslationApi } from './I18nextTranslationApi';\n\n/** @public */\nexport interface PseudolocalizationOptions {\n wrapped?: boolean;\n languageToPseudo?: string;\n}\n\nfunction mergePostProcess(i18n: I18n): void {\n const cur = i18n.options.postProcess;\n let list: string[];\n if (!cur) {\n list = [];\n } else if (Array.isArray(cur)) {\n list = [...cur];\n } else {\n list = [cur as string];\n }\n if (!list.includes('pseudo')) {\n list.push('pseudo');\n }\n i18n.options.postProcess = list;\n}\n\n/**\n * Attaches the i18next-pseudo post-processor to a translation API's i18n\n * instance. Call once after {@link I18nextTranslationApi.create}.\n *\n * @public\n */\nexport function attachPseudolocalization(\n translationApi: I18nextTranslationApi,\n options?: PseudolocalizationOptions,\n): void {\n const i18n = translationApi.getI18nInstance();\n const wrapped = options?.wrapped ?? true;\n const languageToPseudo =\n options?.languageToPseudo ?? i18n.resolvedLanguage ?? i18n.language ?? 'en';\n\n const pseudo = new Pseudo({\n enabled: true,\n wrapped,\n languageToPseudo,\n });\n\n i18n.use(pseudo);\n mergePostProcess(i18n);\n\n const syncLanguageToPseudo = () => {\n const next =\n options?.languageToPseudo ??\n i18n.resolvedLanguage ??\n i18n.language ??\n 'en';\n pseudo.configurePseudo({ languageToPseudo: next });\n };\n\n syncLanguageToPseudo();\n i18n.on('languageChanged', syncLanguageToPseudo);\n}\n\n/**\n * Convenience wrapper that enables pseudo-localization when activated via\n * URL query parameter (`?pseudolocalization=true`) or app-config\n * (`i18n.pseudolocalization.enabled: true`).\n *\n * @public\n */\nexport function attachPseudolocalizationIfEnabled(\n translationApi: I18nextTranslationApi,\n configApi: ConfigApi,\n locationSearch?: string,\n): void {\n const params = new URLSearchParams(locationSearch ?? window.location.search);\n const fromQuery = params.get('pseudolocalization') === 'true';\n const fromConfig =\n configApi.getOptionalBoolean('i18n.pseudolocalization.enabled') ?? false;\n\n if (!fromQuery && !fromConfig) {\n return;\n }\n\n const languageToPseudo =\n params.get('lng') ??\n configApi.getOptionalString('i18n.pseudolocalization.language') ??\n undefined;\n\n attachPseudolocalization(translationApi, { languageToPseudo });\n}\n"],"names":[],"mappings":";;AA4BA,SAAS,iBAAiB,IAAA,EAAkB;AAC1C,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,WAAA;AACzB,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,IAAA,GAAO,EAAC;AAAA,EACV,CAAA,MAAA,IAAW,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC7B,IAAA,IAAA,GAAO,CAAC,GAAG,GAAG,CAAA;AAAA,EAChB,CAAA,MAAO;AACL,IAAA,IAAA,GAAO,CAAC,GAAa,CAAA;AAAA,EACvB;AACA,EAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC5B,IAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAAA,EACpB;AACA,EAAA,IAAA,CAAK,QAAQ,WAAA,GAAc,IAAA;AAC7B;AAQO,SAAS,wBAAA,CACd,gBACA,OAAA,EACM;AACN,EAAA,MAAM,IAAA,GAAO,eAAe,eAAA,EAAgB;AAC5C,EAAA,MAAM,OAAA,GAAU,SAAS,OAAA,IAAW,IAAA;AACpC,EAAA,MAAM,mBACJ,OAAA,EAAS,gBAAA,IAAoB,IAAA,CAAK,gBAAA,IAAoB,KAAK,QAAA,IAAY,IAAA;AAEzE,EAAA,MAAM,MAAA,GAAS,IAAI,MAAA,CAAO;AAAA,IACxB,OAAA,EAAS,IAAA;AAAA,IACT,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,IAAA,CAAK,IAAI,MAAM,CAAA;AACf,EAAA,gBAAA,CAAiB,IAAI,CAAA;AAErB,EAAA,MAAM,uBAAuB,MAAM;AACjC,IAAA,MAAM,OACJ,OAAA,EAAS,gBAAA,IACT,IAAA,CAAK,gBAAA,IACL,KAAK,QAAA,IACL,IAAA;AACF,IAAA,MAAA,CAAO,eAAA,CAAgB,EAAE,gBAAA,EAAkB,IAAA,EAAM,CAAA;AAAA,EACnD,CAAA;AAEA,EAAA,oBAAA,EAAqB;AACrB,EAAA,IAAA,CAAK,EAAA,CAAG,mBAAmB,oBAAoB,CAAA;AACjD;AASO,SAAS,iCAAA,CACd,cAAA,EACA,SAAA,EACA,cAAA,EACM;AACN,EAAA,MAAM,SAAS,IAAI,eAAA,CAAgB,cAAA,IAAkB,MAAA,CAAO,SAAS,MAAM,CAAA;AAC3E,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,GAAA,CAAI,oBAAoB,CAAA,KAAM,MAAA;AACvD,EAAA,MAAM,UAAA,GACJ,SAAA,CAAU,kBAAA,CAAmB,iCAAiC,CAAA,IAAK,KAAA;AAErE,EAAA,IAAI,CAAC,SAAA,IAAa,CAAC,UAAA,EAAY;AAC7B,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,gBAAA,GACJ,OAAO,GAAA,CAAI,KAAK,KAChB,SAAA,CAAU,iBAAA,CAAkB,kCAAkC,CAAA,IAC9D,MAAA;AAEF,EAAA,wBAAA,CAAyB,cAAA,EAAgB,EAAE,gBAAA,EAAkB,CAAA;AAC/D;;;;"}
@@ -5,6 +5,10 @@ import { appLanguageApiRef, translationApiRef } from '@backstage/core-plugin-api
5
5
  import { useMemo } from 'react';
6
6
  import { useTranslation } from '../hooks/useTranslation.esm.js';
7
7
 
8
+ function getI18nInstance(translationApi) {
9
+ const api = translationApi;
10
+ return typeof api.getI18nInstance === "function" ? api.getI18nInstance() : void 0;
11
+ }
8
12
  const getLanguageDisplayName = (language) => {
9
13
  try {
10
14
  const names = new Intl.DisplayNames([language], {
@@ -29,27 +33,28 @@ const LoadedTranslationsTable = () => {
29
33
  const { t } = useTranslation();
30
34
  const appLanguageApi = useApi(appLanguageApiRef);
31
35
  const translationApi = useApi(translationApiRef);
32
- const i18n = translationApi.getI18nInstance();
33
- const resources = i18n.store.data;
36
+ const i18n = getI18nInstance(translationApi);
37
+ const resources = i18n?.store.data;
34
38
  const showLanguages = useMemo(
35
- () => [
39
+ () => i18n ? [
36
40
  "en",
37
41
  ...appLanguageApi.getAvailableLanguages().languages.filter((lang) => lang !== "en")
38
- ],
39
- [appLanguageApi]
42
+ ] : [],
43
+ [appLanguageApi, i18n]
40
44
  );
41
45
  const columns = useMemo(
42
- () => [
46
+ () => i18n ? [
43
47
  { title: t("table.headers.refId"), field: "ref" },
44
48
  { title: t("table.headers.key"), field: "key" },
45
49
  ...showLanguages.map((lang) => ({
46
50
  title: getLanguageDisplayName(lang),
47
51
  field: lang
48
52
  }))
49
- ],
50
- [showLanguages, t]
53
+ ] : [],
54
+ [showLanguages, t, i18n]
51
55
  );
52
56
  const data = useMemo(() => {
57
+ if (!resources) return [];
53
58
  const rows = [];
54
59
  if (resources.en) {
55
60
  for (const ref of Object.keys(resources.en)) {
@@ -104,6 +109,17 @@ const LoadedTranslationsTable = () => {
104
109
  }
105
110
  return rows;
106
111
  }, [resources, showLanguages]);
112
+ if (!i18n) {
113
+ return /* @__PURE__ */ jsx(
114
+ Table,
115
+ {
116
+ title: t("table.title", { count: "0" }),
117
+ columns: [],
118
+ data: [],
119
+ emptyContent: /* @__PURE__ */ jsx("span", { children: "Translation resource data is not available with the built-in TranslationApi." })
120
+ }
121
+ );
122
+ }
107
123
  return /* @__PURE__ */ jsx(
108
124
  Table,
109
125
  {
@@ -1 +1 @@
1
- {"version":3,"file":"LoadedTranslationsTable.esm.js","sources":["../../src/components/LoadedTranslationsTable.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Table } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n appLanguageApiRef,\n translationApiRef,\n} from '@backstage/core-plugin-api/alpha';\n\nimport type { i18n as I18n, Resource } from 'i18next';\nimport { useMemo } from 'react';\nimport { useTranslation } from '../hooks/useTranslation';\n\ninterface Row {\n ref: string;\n key: string;\n [lang: string]: string | undefined;\n}\n\n// From https://github.com/backstage/backstage/blob/89f191b72c154e3f0bde70548a7bccbb23af8de0/plugins/user-settings/src/components/General/UserSettingsLanguageToggle.tsx#L68\nconst getLanguageDisplayName = (language: string) => {\n try {\n const names = new Intl.DisplayNames([language], {\n type: 'language',\n });\n const languageDisplayName = names.of(language);\n if (languageDisplayName) {\n return `${languageDisplayName} (${language})`;\n }\n return language;\n } catch (err) {\n return language;\n }\n};\n\nconst getTranslation = (\n resources: Resource,\n lang: string,\n ref: string,\n key: string,\n) => {\n const resourceLang = resources[lang];\n const resourceKey = resourceLang?.[ref];\n const translation =\n typeof resourceKey === 'object' ? resourceKey?.[key] : undefined;\n return translation;\n};\n\nexport const LoadedTranslationsTable = () => {\n const { t } = useTranslation();\n const appLanguageApi = useApi(appLanguageApiRef);\n const translationApi = useApi(translationApiRef);\n const i18n: I18n = (translationApi as any).getI18nInstance();\n\n const resources = i18n.store.data;\n\n // FIXME: the available languages are not yet loaded...\n const showLanguages = useMemo(\n () => [\n 'en',\n ...appLanguageApi\n .getAvailableLanguages()\n .languages.filter(lang => lang !== 'en'),\n ],\n [appLanguageApi],\n );\n\n const columns = useMemo(\n () => [\n { title: t('table.headers.refId'), field: 'ref' },\n { title: t('table.headers.key'), field: 'key' },\n ...showLanguages.map(lang => ({\n title: getLanguageDisplayName(lang),\n field: lang,\n })),\n ],\n [showLanguages, t],\n );\n\n const data = useMemo(() => {\n const rows: Row[] = [];\n\n // Iterate first over primary language english\n if (resources.en) {\n for (const ref of Object.keys(resources.en)) {\n for (const key of Object.keys(resources.en[ref])) {\n rows.push(\n showLanguages.reduce(\n (row, lang) => {\n row[lang] = getTranslation(resources, lang, ref, key);\n return row;\n },\n {\n ref,\n key,\n } as Row,\n ),\n );\n }\n }\n }\n\n // Check if other languages have additional keys\n for (const otherLang of Object.keys(resources)) {\n if (otherLang === 'en') {\n continue;\n }\n for (const ref of Object.keys(resources[otherLang])) {\n for (const key of Object.keys(resources[otherLang][ref])) {\n // if the key exists in english, then it was already added\n const resourceLang = resources.en;\n const resourceKey = resourceLang?.[ref];\n if (typeof resourceKey === 'object' && resourceKey?.[key]) {\n continue;\n }\n // Check if we already have a row for this ref/key combination\n // from another language. If yes, then we can skip this as well.\n const existingRow = rows.find(r => r.ref === ref && r.key === key);\n if (existingRow) {\n showLanguages.forEach(lang => {\n existingRow[lang] = getTranslation(resources, lang, ref, key);\n });\n continue;\n }\n // Otherwise add a new row and include all languages\n rows.push(\n showLanguages.reduce(\n (row, lang) => {\n row[lang] = getTranslation(resources, lang, ref, key);\n return row;\n },\n {\n ref,\n key,\n } as Row,\n ),\n );\n }\n }\n }\n\n return rows;\n }, [resources, showLanguages]);\n\n return (\n <Table\n title={t('table.title' as any, { count: data.length.toString() })}\n columns={columns}\n data={data}\n options={{\n pageSize: 10,\n pageSizeOptions: [10, 25, 100],\n }}\n />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LoadedTranslationsTable.esm.js","sources":["../../src/components/LoadedTranslationsTable.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Table } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n appLanguageApiRef,\n translationApiRef,\n} from '@backstage/core-plugin-api/alpha';\n\nimport type { i18n as I18n, Resource } from 'i18next';\nimport { useMemo } from 'react';\nimport { useTranslation } from '../hooks/useTranslation';\n\nfunction getI18nInstance(translationApi: unknown): I18n | undefined {\n const api = translationApi as { getI18nInstance?: () => I18n };\n return typeof api.getI18nInstance === 'function'\n ? api.getI18nInstance()\n : undefined;\n}\n\ninterface Row {\n ref: string;\n key: string;\n [lang: string]: string | undefined;\n}\n\n// From https://github.com/backstage/backstage/blob/89f191b72c154e3f0bde70548a7bccbb23af8de0/plugins/user-settings/src/components/General/UserSettingsLanguageToggle.tsx#L68\nconst getLanguageDisplayName = (language: string) => {\n try {\n const names = new Intl.DisplayNames([language], {\n type: 'language',\n });\n const languageDisplayName = names.of(language);\n if (languageDisplayName) {\n return `${languageDisplayName} (${language})`;\n }\n return language;\n } catch (err) {\n return language;\n }\n};\n\nconst getTranslation = (\n resources: Resource,\n lang: string,\n ref: string,\n key: string,\n) => {\n const resourceLang = resources[lang];\n const resourceKey = resourceLang?.[ref];\n const translation =\n typeof resourceKey === 'object' ? resourceKey?.[key] : undefined;\n return translation;\n};\n\nexport const LoadedTranslationsTable = () => {\n const { t } = useTranslation();\n const appLanguageApi = useApi(appLanguageApiRef);\n const translationApi = useApi(translationApiRef);\n const i18n = getI18nInstance(translationApi);\n\n const resources = i18n?.store.data;\n\n // FIXME: the available languages are not yet loaded...\n const showLanguages = useMemo(\n () =>\n i18n\n ? [\n 'en',\n ...appLanguageApi\n .getAvailableLanguages()\n .languages.filter(lang => lang !== 'en'),\n ]\n : [],\n [appLanguageApi, i18n],\n );\n\n const columns = useMemo(\n () =>\n i18n\n ? [\n { title: t('table.headers.refId'), field: 'ref' },\n { title: t('table.headers.key'), field: 'key' },\n ...showLanguages.map(lang => ({\n title: getLanguageDisplayName(lang),\n field: lang,\n })),\n ]\n : [],\n [showLanguages, t, i18n],\n );\n\n const data = useMemo(() => {\n if (!resources) return [];\n const rows: Row[] = [];\n\n if (resources.en) {\n for (const ref of Object.keys(resources.en)) {\n for (const key of Object.keys(resources.en[ref])) {\n rows.push(\n showLanguages.reduce(\n (row, lang) => {\n row[lang] = getTranslation(resources, lang, ref, key);\n return row;\n },\n {\n ref,\n key,\n } as Row,\n ),\n );\n }\n }\n }\n\n for (const otherLang of Object.keys(resources)) {\n if (otherLang === 'en') {\n continue;\n }\n for (const ref of Object.keys(resources[otherLang])) {\n for (const key of Object.keys(resources[otherLang][ref])) {\n const resourceLang = resources.en;\n const resourceKey = resourceLang?.[ref];\n if (typeof resourceKey === 'object' && resourceKey?.[key]) {\n continue;\n }\n const existingRow = rows.find(r => r.ref === ref && r.key === key);\n if (existingRow) {\n showLanguages.forEach(lang => {\n existingRow[lang] = getTranslation(resources, lang, ref, key);\n });\n continue;\n }\n rows.push(\n showLanguages.reduce(\n (row, lang) => {\n row[lang] = getTranslation(resources, lang, ref, key);\n return row;\n },\n {\n ref,\n key,\n } as Row,\n ),\n );\n }\n }\n }\n\n return rows;\n }, [resources, showLanguages]);\n\n if (!i18n) {\n return (\n <Table\n title={t('table.title' as any, { count: '0' })}\n columns={[]}\n data={[]}\n emptyContent={\n <span>\n Translation resource data is not available with the built-in\n TranslationApi.\n </span>\n }\n />\n );\n }\n\n return (\n <Table\n title={t('table.title' as any, { count: data.length.toString() })}\n columns={columns}\n data={data}\n options={{\n pageSize: 10,\n pageSizeOptions: [10, 25, 100],\n }}\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import { useRef, useEffect } from 'react';
3
+ import { useApi, configApiRef } from '@backstage/core-plugin-api';
4
+ import { translationApiRef } from '@backstage/core-plugin-api/alpha';
5
+ import { attachPseudolocalizationIfEnabled } from '../apis/pseudolocalization.esm.js';
6
+
7
+ const PseudoLocalizationProvider = ({
8
+ children
9
+ }) => {
10
+ const configApi = useApi(configApiRef);
11
+ const translationApi = useApi(translationApiRef);
12
+ const attached = useRef(false);
13
+ useEffect(() => {
14
+ if (attached.current) {
15
+ return;
16
+ }
17
+ attached.current = true;
18
+ const api = translationApi;
19
+ if (typeof api.getI18nInstance !== "function") {
20
+ return;
21
+ }
22
+ attachPseudolocalizationIfEnabled(api, configApi);
23
+ }, [translationApi, configApi]);
24
+ return /* @__PURE__ */ jsx(Fragment, { children });
25
+ };
26
+
27
+ export { PseudoLocalizationProvider };
28
+ //# sourceMappingURL=PseudoLocalizationProvider.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PseudoLocalizationProvider.esm.js","sources":["../../src/components/PseudoLocalizationProvider.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type PropsWithChildren, useEffect, useRef } from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { translationApiRef } from '@backstage/core-plugin-api/alpha';\n\nimport { attachPseudolocalizationIfEnabled } from '../apis/pseudolocalization';\n\n/**\n * Provider component that enables pseudo-localization when activated via\n * URL query parameter (`?pseudolocalization=true`) or app-config\n * (`i18n.pseudolocalization.enabled: true`).\n *\n * Requires the TranslationApi implementation to expose `getI18nInstance()`.\n * When the built-in NFS API is used (which does not have this method),\n * pseudo-localization is silently skipped.\n *\n * @public\n */\nexport const PseudoLocalizationProvider = ({\n children,\n}: PropsWithChildren<{}>) => {\n const configApi = useApi(configApiRef);\n const translationApi = useApi(translationApiRef);\n const attached = useRef(false);\n\n useEffect(() => {\n if (attached.current) {\n return;\n }\n attached.current = true;\n\n const api = translationApi as unknown as {\n getI18nInstance?: () => unknown;\n };\n if (typeof api.getI18nInstance !== 'function') {\n return;\n }\n attachPseudolocalizationIfEnabled(api as any, configApi);\n }, [translationApi, configApi]);\n\n return <>{children}</>;\n};\n"],"names":[],"mappings":";;;;;;AAkCO,MAAM,6BAA6B,CAAC;AAAA,EACzC;AACF,CAAA,KAA6B;AAC3B,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,cAAA,GAAiB,OAAO,iBAAiB,CAAA;AAC/C,EAAA,MAAM,QAAA,GAAW,OAAO,KAAK,CAAA;AAE7B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA;AAAA,IACF;AACA,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAEnB,IAAA,MAAM,GAAA,GAAM,cAAA;AAGZ,IAAA,IAAI,OAAO,GAAA,CAAI,eAAA,KAAoB,UAAA,EAAY;AAC7C,MAAA;AAAA,IACF;AACA,IAAA,iCAAA,CAAkC,KAAY,SAAS,CAAA;AAAA,EACzD,CAAA,EAAG,CAAC,cAAA,EAAgB,SAAS,CAAC,CAAA;AAE9B,EAAA,uCAAU,QAAA,EAAS,CAAA;AACrB;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,46 +1,83 @@
1
- import { TranslationApi, AppLanguageApi, TranslationMessages, TranslationResource, TranslationRef, TranslationSnapshot } from '@backstage/core-plugin-api/alpha';
2
- import { i18n } from 'i18next';
3
- import { Observable } from '@backstage/types';
1
+ import * as react from 'react';
4
2
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
6
- import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
7
-
8
- /** @public */
9
- interface I18nextTranslationApiOptions {
10
- languageApi: AppLanguageApi;
11
- resources?: Array<TranslationMessages | TranslationResource>;
12
- }
13
- /** @public */
14
- declare class I18nextTranslationApi implements TranslationApi {
15
- #private;
16
- static create(options: I18nextTranslationApiOptions): I18nextTranslationApi;
17
- private constructor();
18
- getI18nInstance(): i18n;
19
- getTranslation<TMessages extends {
20
- [key in string]: string;
21
- }>(translationRef: TranslationRef<string, TMessages>): TranslationSnapshot<TMessages>;
22
- translation$<TMessages extends {
23
- [key in string]: string;
24
- }>(translationRef: TranslationRef<string, TMessages>): Observable<TranslationSnapshot<TMessages>>;
25
- }
3
+ export { translationsPluginTranslationRef, translationsPluginTranslations } from './alpha.js';
26
4
 
27
5
  /**
6
+ * The Translations frontend plugin for the new Backstage frontend system.
7
+ *
28
8
  * @public
29
9
  */
30
- declare const translationsPlugin: _backstage_core_plugin_api.BackstagePlugin<{
31
- root: _backstage_core_plugin_api.RouteRef<undefined>;
32
- }, {}, {}>;
10
+ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{}, {}, {
11
+ "page:translations": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
12
+ kind: "page";
13
+ name: undefined;
14
+ config: {
15
+ path: string | undefined;
16
+ title: string | undefined;
17
+ };
18
+ configInput: {
19
+ path?: string | undefined;
20
+ title?: string | undefined;
21
+ };
22
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
23
+ optional: true;
24
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
25
+ optional: true;
26
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
27
+ optional: true;
28
+ }>;
29
+ inputs: {
30
+ pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
31
+ optional: true;
32
+ }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
33
+ optional: true;
34
+ }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
35
+ optional: true;
36
+ }>, {
37
+ singleton: false;
38
+ optional: false;
39
+ internal: false;
40
+ }>;
41
+ };
42
+ params: {
43
+ path: string;
44
+ title?: string;
45
+ icon?: _backstage_frontend_plugin_api.IconElement;
46
+ loader?: () => Promise<react.JSX.Element>;
47
+ routeRef?: _backstage_frontend_plugin_api.RouteRef;
48
+ noHeader?: boolean;
49
+ };
50
+ }>;
51
+ "translation:translations/translations-i18n": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
52
+ kind: "translation";
53
+ name: "translations-i18n";
54
+ config: {};
55
+ configInput: {};
56
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.TranslationResource<string> | _backstage_frontend_plugin_api.TranslationMessages<string, {
57
+ [x: string]: string;
58
+ }, boolean>, "core.translation.translation", {}>;
59
+ inputs: {};
60
+ params: {
61
+ resource: _backstage_frontend_plugin_api.TranslationResource | _backstage_frontend_plugin_api.TranslationMessages;
62
+ };
63
+ }>;
64
+ }>;
65
+
33
66
  /**
67
+ * Module that replaces the built-in TranslationApi with one that
68
+ * exposes `getI18nInstance()`, needed by the translations debug page
69
+ * and pseudo-localization. Also attaches pseudo-localization when
70
+ * enabled via app-config or URL param.
71
+ *
34
72
  * @public
35
73
  */
36
- declare const TranslationsPage: () => react_jsx_runtime.JSX.Element;
74
+ declare const translationsApiModule: _backstage_frontend_plugin_api.FrontendModule;
37
75
  /**
38
- * Export Translation Keys
76
+ * Pseudo-localization module for OFS / dynamic plugins.
77
+ * In NFS, pseudo-localization is handled by `translationsApiModule` instead.
78
+ *
39
79
  * @public
40
80
  */
41
- declare const ExportTranslationKeys: ({ resources, }: {
42
- resources: _backstage_frontend_plugin_api.TranslationRef[];
43
- }) => react_jsx_runtime.JSX.Element;
81
+ declare const translationsPseudoLocalizationModule: _backstage_frontend_plugin_api.FrontendModule;
44
82
 
45
- export { ExportTranslationKeys, I18nextTranslationApi, TranslationsPage, translationsPlugin };
46
- export type { I18nextTranslationApiOptions };
83
+ export { _default as default, translationsApiModule, translationsPseudoLocalizationModule };
package/dist/index.esm.js CHANGED
@@ -1,3 +1,74 @@
1
- export { I18nextTranslationApi } from './apis/I18nextTranslationApi.esm.js';
2
- export { ExportTranslationKeys, TranslationsPage, translationsPlugin } from './plugin.esm.js';
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { PageBlueprint, ApiBlueprint, configApiRef, appLanguageApiRef, translationApiRef, createExtensionInput, createFrontendPlugin, createFrontendModule } from '@backstage/frontend-plugin-api';
3
+ import ExtensionIcon from '@mui/icons-material/Extension';
4
+ import { TranslationBlueprint, AppRootWrapperBlueprint } from '@backstage/plugin-app-react';
5
+ import { translationsPluginTranslations } from './translations/index.esm.js';
6
+ import { I18nextTranslationApi } from './apis/I18nextTranslationApi.esm.js';
7
+ import { attachPseudolocalizationIfEnabled } from './apis/pseudolocalization.esm.js';
8
+ import { PseudoLocalizationProvider } from './components/PseudoLocalizationProvider.esm.js';
9
+ export { translationsPluginTranslationRef } from './translations/ref.esm.js';
10
+
11
+ const translationsPage = PageBlueprint.make({
12
+ params: {
13
+ path: "/translations",
14
+ title: "Translations",
15
+ icon: /* @__PURE__ */ jsx(ExtensionIcon, {}),
16
+ noHeader: true,
17
+ loader: () => import('./components/TranslationsPage.esm.js').then((m) => /* @__PURE__ */ jsx(m.TranslationsPage, {}))
18
+ }
19
+ });
20
+ const i18nTranslation = TranslationBlueprint.make({
21
+ name: "translations-i18n",
22
+ params: {
23
+ resource: translationsPluginTranslations
24
+ }
25
+ });
26
+ const translationsApiExtension = ApiBlueprint.makeWithOverrides({
27
+ name: "translations",
28
+ inputs: {
29
+ translations: createExtensionInput(
30
+ [TranslationBlueprint.dataRefs.translation],
31
+ { replaces: [{ id: "app", input: "translations" }], internal: true }
32
+ )
33
+ },
34
+ factory: (originalFactory, { inputs }) => {
35
+ return originalFactory(
36
+ (defineParams) => defineParams({
37
+ api: translationApiRef,
38
+ deps: { languageApi: appLanguageApiRef, configApi: configApiRef },
39
+ factory: ({ languageApi, configApi }) => {
40
+ const resources = inputs.translations.map(
41
+ (i) => i.get(TranslationBlueprint.dataRefs.translation)
42
+ );
43
+ const api = I18nextTranslationApi.create({
44
+ languageApi,
45
+ resources
46
+ });
47
+ attachPseudolocalizationIfEnabled(api, configApi);
48
+ return api;
49
+ }
50
+ })
51
+ );
52
+ }
53
+ });
54
+ const pseudoLocalizationWrapper = AppRootWrapperBlueprint.make({
55
+ name: "pseudo-localization",
56
+ params: {
57
+ component: PseudoLocalizationProvider
58
+ }
59
+ });
60
+ var index = createFrontendPlugin({
61
+ pluginId: "translations",
62
+ extensions: [translationsPage, i18nTranslation]
63
+ });
64
+ const translationsApiModule = createFrontendModule({
65
+ pluginId: "app",
66
+ extensions: [translationsApiExtension]
67
+ });
68
+ const translationsPseudoLocalizationModule = createFrontendModule({
69
+ pluginId: "app",
70
+ extensions: [pseudoLocalizationWrapper]
71
+ });
72
+
73
+ export { index as default, translationsApiModule, translationsPluginTranslations, translationsPseudoLocalizationModule };
3
74
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/index.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ApiBlueprint,\n appLanguageApiRef,\n configApiRef,\n createExtensionInput,\n createFrontendModule,\n createFrontendPlugin,\n PageBlueprint,\n translationApiRef,\n} from '@backstage/frontend-plugin-api';\nimport ExtensionIcon from '@mui/icons-material/Extension';\nimport {\n AppRootWrapperBlueprint,\n TranslationBlueprint,\n} from '@backstage/plugin-app-react';\n\nimport {\n translationsPluginTranslationRef,\n translationsPluginTranslations,\n} from './translations';\nimport { I18nextTranslationApi } from './apis/I18nextTranslationApi';\nimport { attachPseudolocalizationIfEnabled } from './apis/pseudolocalization';\nimport { PseudoLocalizationProvider } from './components/PseudoLocalizationProvider';\n\nconst translationsPage = PageBlueprint.make({\n params: {\n path: '/translations',\n title: 'Translations',\n icon: <ExtensionIcon />,\n noHeader: true,\n loader: () =>\n import('./components/TranslationsPage').then(m => <m.TranslationsPage />),\n },\n});\n\nconst i18nTranslation = TranslationBlueprint.make({\n name: 'translations-i18n',\n params: {\n resource: translationsPluginTranslations,\n },\n});\n\nconst translationsApiExtension = ApiBlueprint.makeWithOverrides({\n name: 'translations',\n inputs: {\n translations: createExtensionInput(\n [TranslationBlueprint.dataRefs.translation],\n { replaces: [{ id: 'app', input: 'translations' }], internal: true },\n ),\n },\n factory: (originalFactory, { inputs }) => {\n return originalFactory(defineParams =>\n defineParams({\n api: translationApiRef,\n deps: { languageApi: appLanguageApiRef, configApi: configApiRef },\n factory: ({ languageApi, configApi }) => {\n const resources = inputs.translations.map(i =>\n i.get(TranslationBlueprint.dataRefs.translation),\n );\n const api = I18nextTranslationApi.create({\n languageApi,\n resources,\n });\n attachPseudolocalizationIfEnabled(api, configApi);\n return api;\n },\n }),\n );\n },\n});\n\nconst pseudoLocalizationWrapper = AppRootWrapperBlueprint.make({\n name: 'pseudo-localization',\n params: {\n component: PseudoLocalizationProvider,\n },\n});\n\n/**\n * The Translations frontend plugin for the new Backstage frontend system.\n *\n * @public\n */\nexport default createFrontendPlugin({\n pluginId: 'translations',\n extensions: [translationsPage, i18nTranslation],\n});\n\n/**\n * Module that replaces the built-in TranslationApi with one that\n * exposes `getI18nInstance()`, needed by the translations debug page\n * and pseudo-localization. Also attaches pseudo-localization when\n * enabled via app-config or URL param.\n *\n * @public\n */\nexport const translationsApiModule = createFrontendModule({\n pluginId: 'app',\n extensions: [translationsApiExtension],\n});\n\n/**\n * Pseudo-localization module for OFS / dynamic plugins.\n * In NFS, pseudo-localization is handled by `translationsApiModule` instead.\n *\n * @public\n */\nexport const translationsPseudoLocalizationModule = createFrontendModule({\n pluginId: 'app',\n extensions: [pseudoLocalizationWrapper],\n});\n\nexport { translationsPluginTranslationRef, translationsPluginTranslations };\n"],"names":[],"mappings":";;;;;;;;;;AAwCA,MAAM,gBAAA,GAAmB,cAAc,IAAA,CAAK;AAAA,EAC1C,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,eAAA;AAAA,IACN,KAAA,EAAO,cAAA;AAAA,IACP,IAAA,sBAAO,aAAA,EAAA,EAAc,CAAA;AAAA,IACrB,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,MACN,OAAO,sCAA+B,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAAK,GAAA,CAAC,CAAA,CAAE,gBAAA,EAAF,EAAmB,CAAE;AAAA;AAE9E,CAAC,CAAA;AAED,MAAM,eAAA,GAAkB,qBAAqB,IAAA,CAAK;AAAA,EAChD,IAAA,EAAM,mBAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,QAAA,EAAU;AAAA;AAEd,CAAC,CAAA;AAED,MAAM,wBAAA,GAA2B,aAAa,iBAAA,CAAkB;AAAA,EAC9D,IAAA,EAAM,cAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,YAAA,EAAc,oBAAA;AAAA,MACZ,CAAC,oBAAA,CAAqB,QAAA,CAAS,WAAW,CAAA;AAAA,MAC1C,EAAE,QAAA,EAAU,CAAC,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,cAAA,EAAgB,CAAA,EAAG,QAAA,EAAU,IAAA;AAAK;AACrE,GACF;AAAA,EACA,OAAA,EAAS,CAAC,eAAA,EAAiB,EAAE,QAAO,KAAM;AACxC,IAAA,OAAO,eAAA;AAAA,MAAgB,kBACrB,YAAA,CAAa;AAAA,QACX,GAAA,EAAK,iBAAA;AAAA,QACL,IAAA,EAAM,EAAE,WAAA,EAAa,iBAAA,EAAmB,WAAW,YAAA,EAAa;AAAA,QAChE,OAAA,EAAS,CAAC,EAAE,WAAA,EAAa,WAAU,KAAM;AACvC,UAAA,MAAM,SAAA,GAAY,OAAO,YAAA,CAAa,GAAA;AAAA,YAAI,CAAA,CAAA,KACxC,CAAA,CAAE,GAAA,CAAI,oBAAA,CAAqB,SAAS,WAAW;AAAA,WACjD;AACA,UAAA,MAAM,GAAA,GAAM,sBAAsB,MAAA,CAAO;AAAA,YACvC,WAAA;AAAA,YACA;AAAA,WACD,CAAA;AACD,UAAA,iCAAA,CAAkC,KAAK,SAAS,CAAA;AAChD,UAAA,OAAO,GAAA;AAAA,QACT;AAAA,OACD;AAAA,KACH;AAAA,EACF;AACF,CAAC,CAAA;AAED,MAAM,yBAAA,GAA4B,wBAAwB,IAAA,CAAK;AAAA,EAC7D,IAAA,EAAM,qBAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,SAAA,EAAW;AAAA;AAEf,CAAC,CAAA;AAOD,YAAe,oBAAA,CAAqB;AAAA,EAClC,QAAA,EAAU,cAAA;AAAA,EACV,UAAA,EAAY,CAAC,gBAAA,EAAkB,eAAe;AAChD,CAAC,CAAA;AAUM,MAAM,wBAAwB,oBAAA,CAAqB;AAAA,EACxD,QAAA,EAAU,KAAA;AAAA,EACV,UAAA,EAAY,CAAC,wBAAwB;AACvC,CAAC;AAQM,MAAM,uCAAuC,oBAAA,CAAqB;AAAA,EACvE,QAAA,EAAU,KAAA;AAAA,EACV,UAAA,EAAY,CAAC,yBAAyB;AACxC,CAAC;;;;"}
@@ -0,0 +1,82 @@
1
+ import { TranslationApi, AppLanguageApi, TranslationMessages, TranslationResource, TranslationRef, TranslationSnapshot } from '@backstage/core-plugin-api/alpha';
2
+ import { i18n } from 'i18next';
3
+ import { Observable } from '@backstage/types';
4
+ import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
5
+ import { ConfigApi } from '@backstage/core-plugin-api';
6
+ import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
7
+ import * as react_jsx_runtime from 'react/jsx-runtime';
8
+ import { PropsWithChildren } from 'react';
9
+
10
+ /** @public */
11
+ interface I18nextTranslationApiOptions {
12
+ languageApi: AppLanguageApi;
13
+ resources?: Array<TranslationMessages | TranslationResource>;
14
+ }
15
+ /** @public */
16
+ declare class I18nextTranslationApi implements TranslationApi {
17
+ #private;
18
+ static create(options: I18nextTranslationApiOptions): I18nextTranslationApi;
19
+ private constructor();
20
+ getI18nInstance(): i18n;
21
+ getTranslation<TMessages extends {
22
+ [key in string]: string;
23
+ }>(translationRef: TranslationRef<string, TMessages>): TranslationSnapshot<TMessages>;
24
+ translation$<TMessages extends {
25
+ [key in string]: string;
26
+ }>(translationRef: TranslationRef<string, TMessages>): Observable<TranslationSnapshot<TMessages>>;
27
+ }
28
+
29
+ /** @public */
30
+ interface PseudolocalizationOptions {
31
+ wrapped?: boolean;
32
+ languageToPseudo?: string;
33
+ }
34
+ /**
35
+ * Attaches the i18next-pseudo post-processor to a translation API's i18n
36
+ * instance. Call once after {@link I18nextTranslationApi.create}.
37
+ *
38
+ * @public
39
+ */
40
+ declare function attachPseudolocalization(translationApi: I18nextTranslationApi, options?: PseudolocalizationOptions): void;
41
+ /**
42
+ * Convenience wrapper that enables pseudo-localization when activated via
43
+ * URL query parameter (`?pseudolocalization=true`) or app-config
44
+ * (`i18n.pseudolocalization.enabled: true`).
45
+ *
46
+ * @public
47
+ */
48
+ declare function attachPseudolocalizationIfEnabled(translationApi: I18nextTranslationApi, configApi: ConfigApi, locationSearch?: string): void;
49
+
50
+ /**
51
+ * @public
52
+ */
53
+ declare const translationsPlugin: _backstage_core_plugin_api.BackstagePlugin<{
54
+ root: _backstage_core_plugin_api.RouteRef<undefined>;
55
+ }, {}, {}>;
56
+ /**
57
+ * @public
58
+ */
59
+ declare const TranslationsPage: () => react_jsx_runtime.JSX.Element;
60
+ /**
61
+ * Export Translation Keys
62
+ * @public
63
+ */
64
+ declare const ExportTranslationKeys: ({ resources, }: {
65
+ resources: _backstage_frontend_plugin_api.TranslationRef[];
66
+ }) => react_jsx_runtime.JSX.Element;
67
+
68
+ /**
69
+ * Provider component that enables pseudo-localization when activated via
70
+ * URL query parameter (`?pseudolocalization=true`) or app-config
71
+ * (`i18n.pseudolocalization.enabled: true`).
72
+ *
73
+ * Requires the TranslationApi implementation to expose `getI18nInstance()`.
74
+ * When the built-in NFS API is used (which does not have this method),
75
+ * pseudo-localization is silently skipped.
76
+ *
77
+ * @public
78
+ */
79
+ declare const PseudoLocalizationProvider: ({ children, }: PropsWithChildren<{}>) => react_jsx_runtime.JSX.Element;
80
+
81
+ export { ExportTranslationKeys, I18nextTranslationApi, PseudoLocalizationProvider, TranslationsPage, attachPseudolocalization, attachPseudolocalizationIfEnabled, translationsPlugin };
82
+ export type { I18nextTranslationApiOptions, PseudolocalizationOptions };
@@ -0,0 +1,5 @@
1
+ export { I18nextTranslationApi } from './apis/I18nextTranslationApi.esm.js';
2
+ export { attachPseudolocalization, attachPseudolocalizationIfEnabled } from './apis/pseudolocalization.esm.js';
3
+ export { ExportTranslationKeys, TranslationsPage, translationsPlugin } from './plugin.esm.js';
4
+ export { PseudoLocalizationProvider } from './components/PseudoLocalizationProvider.esm.js';
5
+ //# sourceMappingURL=legacy.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../../src/translations/index.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTranslationResource } from '@backstage/core-plugin-api/alpha';\nimport { translationsPluginTranslationRef } from './ref';\n\n/**\n * @alpha\n *\n */\nexport const translationsPluginTranslations = createTranslationResource({\n ref: translationsPluginTranslationRef,\n translations: {\n de: () => import('./de'),\n es: () => import('./es'),\n fr: () => import('./fr'),\n it: () => import('./it'),\n ja: () => import('./ja'),\n },\n});\n/**\n * @alpha\n */\nexport { translationsPluginTranslationRef };\n"],"names":[],"mappings":";;;AAsBO,MAAM,iCAAiC,yBAAA,CAA0B;AAAA,EACtE,GAAA,EAAK,gCAAA;AAAA,EACL,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM;AAAA;AAE3B,CAAC;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":["../../src/translations/index.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTranslationResource } from '@backstage/core-plugin-api/alpha';\nimport { translationsPluginTranslationRef } from './ref';\n\n/**\n * @public\n */\nexport const translationsPluginTranslations = createTranslationResource({\n ref: translationsPluginTranslationRef,\n translations: {\n de: () => import('./de'),\n es: () => import('./es'),\n fr: () => import('./fr'),\n it: () => import('./it'),\n ja: () => import('./ja'),\n },\n});\n\n/**\n * @public\n */\nexport { translationsPluginTranslationRef };\n"],"names":[],"mappings":";;;AAqBO,MAAM,iCAAiC,yBAAA,CAA0B;AAAA,EACtE,GAAA,EAAK,gCAAA;AAAA,EACL,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM;AAAA;AAE3B,CAAC;;;;"}
@@ -33,7 +33,14 @@ const translationsMessages = {
33
33
  };
34
34
  const translationsPluginTranslationRef = createTranslationRef({
35
35
  id: "plugin.translations",
36
- messages: translationsMessages
36
+ messages: translationsMessages,
37
+ translations: {
38
+ de: () => import('./de.esm.js'),
39
+ es: () => import('./es.esm.js'),
40
+ fr: () => import('./fr.esm.js'),
41
+ it: () => import('./it.esm.js'),
42
+ ja: () => import('./ja.esm.js')
43
+ }
37
44
  });
38
45
 
39
46
  export { translationsMessages, translationsPluginTranslationRef };
@@ -1 +1 @@
1
- {"version":3,"file":"ref.esm.js","sources":["../../src/translations/ref.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/**\n * @alpha\n */\nexport const translationsMessages = {\n page: {\n title: 'Translations',\n subtitle: 'Manage and view loaded translations',\n },\n table: {\n title: 'Loaded translations ({{count}})',\n headers: {\n refId: 'Ref ID',\n key: 'Key',\n },\n options: {\n pageSize: 'Items per page',\n pageSizeOptions: 'Show {{count}} items',\n },\n },\n export: {\n title: 'Translations',\n downloadButton: 'Download default translations (English)',\n filename: 'translations-{{timestamp}}.json',\n },\n common: {\n loading: 'Loading...',\n error: 'An error occurred',\n noData: 'No data available',\n refresh: 'Refresh',\n },\n language: {\n displayFormat: '{{displayName}} ({{code}})',\n },\n};\n/**\n * @alpha\n */\nexport const translationsPluginTranslationRef = createTranslationRef({\n id: 'plugin.translations',\n messages: translationsMessages,\n});\n"],"names":[],"mappings":";;AAoBO,MAAM,oBAAA,GAAuB;AAAA,EAClC,IAAA,EAAM;AAAA,IACJ,KAAA,EAAO,cAAA;AAAA,IACP,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,KAAA,EAAO;AAAA,IACL,KAAA,EAAO,iCAAA;AAAA,IACP,OAAA,EAAS;AAAA,MACP,KAAA,EAAO,QAAA;AAAA,MACP,GAAA,EAAK;AAAA,KACP;AAAA,IACA,OAAA,EAAS;AAAA,MACP,QAAA,EAAU,gBAAA;AAAA,MACV,eAAA,EAAiB;AAAA;AACnB,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,cAAA;AAAA,IACP,cAAA,EAAgB,yCAAA;AAAA,IAChB,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,OAAA,EAAS,YAAA;AAAA,IACT,KAAA,EAAO,mBAAA;AAAA,IACP,MAAA,EAAQ,mBAAA;AAAA,IACR,OAAA,EAAS;AAAA,GACX;AAAA,EACA,QAAA,EAAU;AAAA,IACR,aAAA,EAAe;AAAA;AAEnB;AAIO,MAAM,mCAAmC,oBAAA,CAAqB;AAAA,EACnE,EAAA,EAAI,qBAAA;AAAA,EACJ,QAAA,EAAU;AACZ,CAAC;;;;"}
1
+ {"version":3,"file":"ref.esm.js","sources":["../../src/translations/ref.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/**\n * @public\n */\nexport const translationsMessages = {\n page: {\n title: 'Translations',\n subtitle: 'Manage and view loaded translations',\n },\n table: {\n title: 'Loaded translations ({{count}})',\n headers: {\n refId: 'Ref ID',\n key: 'Key',\n },\n options: {\n pageSize: 'Items per page',\n pageSizeOptions: 'Show {{count}} items',\n },\n },\n export: {\n title: 'Translations',\n downloadButton: 'Download default translations (English)',\n filename: 'translations-{{timestamp}}.json',\n },\n common: {\n loading: 'Loading...',\n error: 'An error occurred',\n noData: 'No data available',\n refresh: 'Refresh',\n },\n language: {\n displayFormat: '{{displayName}} ({{code}})',\n },\n};\n/**\n * @public\n */\nexport const translationsPluginTranslationRef = createTranslationRef({\n id: 'plugin.translations',\n messages: translationsMessages,\n translations: {\n de: () => import('./de') as any,\n es: () => import('./es') as any,\n fr: () => import('./fr') as any,\n it: () => import('./it') as any,\n ja: () => import('./ja') as any,\n },\n});\n"],"names":[],"mappings":";;AAoBO,MAAM,oBAAA,GAAuB;AAAA,EAClC,IAAA,EAAM;AAAA,IACJ,KAAA,EAAO,cAAA;AAAA,IACP,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,KAAA,EAAO;AAAA,IACL,KAAA,EAAO,iCAAA;AAAA,IACP,OAAA,EAAS;AAAA,MACP,KAAA,EAAO,QAAA;AAAA,MACP,GAAA,EAAK;AAAA,KACP;AAAA,IACA,OAAA,EAAS;AAAA,MACP,QAAA,EAAU,gBAAA;AAAA,MACV,eAAA,EAAiB;AAAA;AACnB,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,cAAA;AAAA,IACP,cAAA,EAAgB,yCAAA;AAAA,IAChB,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,OAAA,EAAS,YAAA;AAAA,IACT,KAAA,EAAO,mBAAA;AAAA,IACP,MAAA,EAAQ,mBAAA;AAAA,IACR,OAAA,EAAS;AAAA,GACX;AAAA,EACA,QAAA,EAAU;AAAA,IACR,aAAA,EAAe;AAAA;AAEnB;AAIO,MAAM,mCAAmC,oBAAA,CAAqB;AAAA,EACnE,EAAA,EAAI,qBAAA;AAAA,EACJ,QAAA,EAAU,oBAAA;AAAA,EACV,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM,CAAA;AAAA,IACvB,EAAA,EAAI,MAAM,OAAO,aAAM;AAAA;AAE3B,CAAC;;;;"}
@@ -0,0 +1,4 @@
1
+ export { translationsApiModule as default } from './index.js';
2
+ import 'react';
3
+ import '@backstage/frontend-plugin-api';
4
+ import './alpha.js';
@@ -0,0 +1,2 @@
1
+ export { translationsApiModule as default } from './index.esm.js';
2
+ //# sourceMappingURL=translations-api-module.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations-api-module.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export { translationsPseudoLocalizationModule as default } from './index.js';
2
+ import 'react';
3
+ import '@backstage/frontend-plugin-api';
4
+ import './alpha.js';
@@ -0,0 +1,2 @@
1
+ export { translationsPseudoLocalizationModule as default } from './index.esm.js';
2
+ //# sourceMappingURL=translations-pseudo-localization-module.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations-pseudo-localization-module.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@red-hat-developer-hub/backstage-plugin-translations",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "exports": {
9
9
  ".": {
10
+ "backstage": "@backstage/FrontendPlugin",
10
11
  "import": "./dist/index.esm.js",
11
12
  "types": "./dist/index.d.ts",
12
13
  "default": "./dist/index.esm.js"
@@ -16,6 +17,23 @@
16
17
  "types": "./dist/alpha.d.ts",
17
18
  "default": "./dist/alpha.esm.js"
18
19
  },
20
+ "./legacy": {
21
+ "import": "./dist/legacy.esm.js",
22
+ "types": "./dist/legacy.d.ts",
23
+ "default": "./dist/legacy.esm.js"
24
+ },
25
+ "./translations-api-module": {
26
+ "backstage": "@backstage/FrontendModule",
27
+ "import": "./dist/translations-api-module.esm.js",
28
+ "types": "./dist/translations-api-module.d.ts",
29
+ "default": "./dist/translations-api-module.esm.js"
30
+ },
31
+ "./translations-pseudo-localization-module": {
32
+ "backstage": "@backstage/FrontendModule",
33
+ "import": "./dist/translations-pseudo-localization-module.esm.js",
34
+ "types": "./dist/translations-pseudo-localization-module.d.ts",
35
+ "default": "./dist/translations-pseudo-localization-module.esm.js"
36
+ },
19
37
  "./package.json": "./package.json"
20
38
  },
21
39
  "typesVersions": {
@@ -23,6 +41,15 @@
23
41
  "alpha": [
24
42
  "dist/alpha.d.ts"
25
43
  ],
44
+ "legacy": [
45
+ "dist/legacy.d.ts"
46
+ ],
47
+ "translations-api-module": [
48
+ "dist/translations-api-module.d.ts"
49
+ ],
50
+ "translations-pseudo-localization-module": [
51
+ "dist/translations-pseudo-localization-module.d.ts"
52
+ ],
26
53
  "package.json": [
27
54
  "package.json"
28
55
  ]
@@ -35,17 +62,34 @@
35
62
  "url": "https://github.com/redhat-developer/rhdh-plugins",
36
63
  "directory": "workspaces/translations/plugins/translations"
37
64
  },
65
+ "configSchema": "config.schema.json",
38
66
  "backstage": {
39
67
  "role": "frontend-plugin",
40
68
  "pluginId": "translations",
41
69
  "pluginPackages": [
42
70
  "@red-hat-developer-hub/backstage-plugin-translations",
43
71
  "@red-hat-developer-hub/backstage-plugin-translations-backend"
44
- ]
72
+ ],
73
+ "features": {
74
+ ".": "@backstage/FrontendPlugin",
75
+ "./translations-api-module": "@backstage/FrontendModule",
76
+ "./translations-pseudo-localization-module": "@backstage/FrontendModule"
77
+ }
78
+ },
79
+ "scalprum": {
80
+ "name": "red-hat-developer-hub.backstage-plugin-translations",
81
+ "exposedModules": {
82
+ "PluginRoot": "./src/index.tsx",
83
+ "Alpha": "./src/alpha.ts",
84
+ "Legacy": "./src/legacy.ts",
85
+ "TranslationsApiModule": "./src/translationsApiModuleExport.ts",
86
+ "TranslationsPseudoLocalizationModule": "./src/translationsPseudoLocalizationModuleExport.ts"
87
+ }
45
88
  },
46
89
  "sideEffects": false,
47
90
  "scripts": {
48
91
  "start": "backstage-cli package start",
92
+ "start:legacy": "backstage-cli package start --entrypoint dev/legacy.tsx",
49
93
  "build": "backstage-cli package build",
50
94
  "lint": "backstage-cli package lint",
51
95
  "test": "backstage-cli package test",
@@ -54,24 +98,28 @@
54
98
  "postpack": "backstage-cli package postpack"
55
99
  },
56
100
  "dependencies": {
57
- "@backstage/core-components": "^0.18.8",
58
- "@backstage/core-plugin-api": "^1.12.4",
59
- "@backstage/plugin-user-settings": "^0.9.1",
60
- "@backstage/theme": "^0.7.2",
101
+ "@backstage/core-components": "^0.18.13",
102
+ "@backstage/core-plugin-api": "^1.12.9",
103
+ "@backstage/plugin-user-settings": "^0.9.6",
104
+ "@backstage/theme": "^0.7.3",
61
105
  "@backstage/types": "^1.2.2",
62
106
  "@mui/icons-material": "5.18.0",
63
107
  "@mui/material": "5.18.0",
64
108
  "i18next": "^22.4.15",
109
+ "i18next-pseudo": "^2.2.1",
65
110
  "zen-observable": "^0.10.0"
66
111
  },
67
112
  "peerDependencies": {
68
- "react": "^16.13.1 || ^17.0.0 || ^18.0.0"
113
+ "react": "^16.13.1 || ^17.0.0 || ^18.0.0",
114
+ "react-dom": "*"
69
115
  },
70
116
  "devDependencies": {
71
- "@backstage/cli": "^0.36.0",
72
- "@backstage/core-app-api": "^1.19.6",
73
- "@backstage/dev-utils": "^1.1.21",
74
- "@backstage/test-utils": "^1.7.16",
117
+ "@backstage/cli": "^0.36.5",
118
+ "@backstage/config": "^1.3.8",
119
+ "@backstage/core-app-api": "^1.20.4",
120
+ "@backstage/dev-utils": "^1.1.26",
121
+ "@backstage/frontend-defaults": "^0.5.5",
122
+ "@backstage/test-utils": "^1.7.21",
75
123
  "@testing-library/jest-dom": "^6.0.0",
76
124
  "@testing-library/react": "^14.0.0",
77
125
  "@testing-library/user-event": "^14.0.0",
@@ -80,7 +128,9 @@
80
128
  "react": "^16.13.1 || ^17.0.0 || ^18.0.0"
81
129
  },
82
130
  "files": [
83
- "dist"
131
+ "app-config.dynamic.yaml",
132
+ "dist",
133
+ "config.schema.json"
84
134
  ],
85
135
  "module": "./dist/index.esm.js"
86
136
  }