@backstage/core-app-api 1.10.0-next.0 → 1.10.0-next.2

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,30 @@
1
1
  # @backstage/core-app-api
2
2
 
3
+ ## 1.10.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - 6e30769cc627: Introduced experimental support for internationalization.
8
+
9
+ ### Patch Changes
10
+
11
+ - 8cec7664e146: Removed `@types/node` dependency
12
+ - Updated dependencies
13
+ - @backstage/core-plugin-api@1.6.0-next.2
14
+ - @backstage/config@1.1.0-next.1
15
+ - @backstage/types@1.1.0
16
+ - @backstage/version-bridge@1.0.4
17
+
18
+ ## 1.10.0-next.1
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+ - @backstage/config@1.1.0-next.0
24
+ - @backstage/core-plugin-api@1.6.0-next.1
25
+ - @backstage/types@1.1.0
26
+ - @backstage/version-bridge@1.0.4
27
+
3
28
  ## 1.10.0-next.0
4
29
 
5
30
  ### Minor Changes
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "@backstage/core-app-api",
3
+ "version": "1.10.0-next.2",
4
+ "main": "../dist/alpha.esm.js",
5
+ "module": "../dist/alpha.esm.js",
6
+ "types": "../dist/alpha.d.ts"
7
+ }
@@ -0,0 +1,49 @@
1
+ import { TranslationRef, AppTranslationApi } from '@backstage/core-plugin-api/alpha';
2
+ import { i18n } from 'i18next';
3
+
4
+ /** @alpha */
5
+ type ExperimentalI18n = {
6
+ supportedLanguages: string[];
7
+ fallbackLanguage?: string | string[];
8
+ messages?: Array<{
9
+ ref: TranslationRef;
10
+ messages?: TranslationMessages<TranslationRef>;
11
+ lazyMessages: Record<string, () => Promise<{
12
+ messages: TranslationMessages<TranslationRef>;
13
+ }>>;
14
+ }>;
15
+ };
16
+ /** @alpha */
17
+ declare class AppTranslationApiImpl implements AppTranslationApi {
18
+ private readonly i18n;
19
+ static create(options?: ExperimentalI18n): AppTranslationApiImpl;
20
+ private readonly cache;
21
+ private readonly lazyCache;
22
+ getI18n(): i18n;
23
+ initMessages(options?: ExperimentalI18n): void;
24
+ addResourcesByRef<Messages extends Record<string, string>>(translationRef: TranslationRef<Messages>): void;
25
+ addResources<Messages extends Record<string, string>>(translationRef: TranslationRef<Messages>, initResources?: TranslationMessages<TranslationRef<Messages>>): void;
26
+ addLazyResources<Messages extends Record<string, string>>(translationRef: TranslationRef<Messages>, initResources?: Record<string, () => Promise<{
27
+ messages: TranslationMessages<TranslationRef>;
28
+ }>>): void;
29
+ private constructor();
30
+ }
31
+
32
+ /** @alpha */
33
+ type TranslationMessages<T> = T extends TranslationRef<infer R> ? Record<string, Partial<R>> : never;
34
+ /** @alpha */
35
+ declare function createTranslationResource<T extends TranslationRef>(options: {
36
+ ref: T;
37
+ messages?: TranslationMessages<T>;
38
+ lazyMessages: Record<string, () => Promise<{
39
+ messages: TranslationMessages<T>;
40
+ }>>;
41
+ }): {
42
+ ref: T;
43
+ messages?: TranslationMessages<T> | undefined;
44
+ lazyMessages: Record<string, () => Promise<{
45
+ messages: TranslationMessages<T>;
46
+ }>>;
47
+ };
48
+
49
+ export { AppTranslationApiImpl, ExperimentalI18n, TranslationMessages, createTranslationResource };
@@ -0,0 +1,11 @@
1
+ export { A as AppTranslationApiImpl } from './esm/AppTranslationImpl-8560b672.esm.js';
2
+ import 'i18next';
3
+ import 'react-i18next';
4
+ import 'i18next-browser-languagedetector';
5
+
6
+ function createTranslationResource(options) {
7
+ return options;
8
+ }
9
+
10
+ export { createTranslationResource };
11
+ //# sourceMappingURL=alpha.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.esm.js","sources":["../src/app/TranslationResource.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\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 { TranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/** @alpha */\nexport type TranslationMessages<T> = T extends TranslationRef<infer R>\n ? Record<string, Partial<R>>\n : never;\n\n/** @alpha */\nexport function createTranslationResource<T extends TranslationRef>(options: {\n ref: T;\n messages?: TranslationMessages<T>;\n lazyMessages: Record<\n string,\n () => Promise<{ messages: TranslationMessages<T> }>\n >;\n}) {\n return options;\n}\n"],"names":[],"mappings":";;;;;AAwBO,SAAS,0BAAoD,OAOjE,EAAA;AACD,EAAO,OAAA,OAAA,CAAA;AACT;;;;"}
@@ -0,0 +1,132 @@
1
+ import i18next from 'i18next';
2
+ import { initReactI18next } from 'react-i18next';
3
+ import LanguageDetector from 'i18next-browser-languagedetector';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __publicField = (obj, key, value) => {
8
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
9
+ return value;
10
+ };
11
+ class AppTranslationApiImpl {
12
+ constructor(i18n, options) {
13
+ this.i18n = i18n;
14
+ __publicField(this, "cache", /* @__PURE__ */ new WeakSet());
15
+ __publicField(this, "lazyCache", /* @__PURE__ */ new WeakMap());
16
+ this.initMessages(options);
17
+ }
18
+ static create(options) {
19
+ const i18n = i18next.createInstance().use(initReactI18next);
20
+ i18n.use(LanguageDetector);
21
+ i18n.init({
22
+ fallbackLng: (options == null ? void 0 : options.fallbackLanguage) || "en",
23
+ supportedLngs: (options == null ? void 0 : options.supportedLanguages) || ["en"],
24
+ interpolation: {
25
+ escapeValue: false
26
+ },
27
+ react: {
28
+ bindI18n: "loaded languageChanged"
29
+ }
30
+ });
31
+ return new AppTranslationApiImpl(i18n, options);
32
+ }
33
+ getI18n() {
34
+ return this.i18n;
35
+ }
36
+ initMessages(options) {
37
+ var _a;
38
+ if ((_a = options == null ? void 0 : options.messages) == null ? void 0 : _a.length) {
39
+ options.messages.forEach((appMessage) => {
40
+ if (appMessage.messages) {
41
+ this.addResources(appMessage.ref, appMessage.messages);
42
+ }
43
+ if (appMessage.lazyMessages) {
44
+ this.addLazyResources(appMessage.ref, appMessage.lazyMessages);
45
+ }
46
+ });
47
+ }
48
+ }
49
+ addResourcesByRef(translationRef) {
50
+ this.addResources(translationRef);
51
+ this.addLazyResources(translationRef);
52
+ }
53
+ addResources(translationRef, initResources) {
54
+ const resources = initResources || translationRef.getResources();
55
+ if (!resources || this.cache.has(translationRef)) {
56
+ return;
57
+ }
58
+ this.cache.add(translationRef);
59
+ Object.entries(resources).forEach(([language, messages]) => {
60
+ this.i18n.addResourceBundle(
61
+ language,
62
+ translationRef.getId(),
63
+ messages,
64
+ true,
65
+ false
66
+ );
67
+ });
68
+ }
69
+ addLazyResources(translationRef, initResources) {
70
+ let cache = this.lazyCache.get(translationRef);
71
+ if (!cache) {
72
+ cache = /* @__PURE__ */ new Set();
73
+ this.lazyCache.set(translationRef, cache);
74
+ }
75
+ const {
76
+ language: currentLanguage,
77
+ services,
78
+ options,
79
+ addResourceBundle,
80
+ reloadResources
81
+ } = this.i18n;
82
+ if (cache.has(currentLanguage)) {
83
+ return;
84
+ }
85
+ const namespace = translationRef.getId();
86
+ const lazyResources = initResources || translationRef.getLazyResources();
87
+ const fallbackLanguages = services.languageUtils.getFallbackCodes(
88
+ options.fallbackLng,
89
+ currentLanguage
90
+ );
91
+ Promise.allSettled(
92
+ [...fallbackLanguages, currentLanguage].map(addLanguage)
93
+ ).then((results) => {
94
+ if (results.some((result) => result.status === "fulfilled")) {
95
+ this.i18n.emit("loaded");
96
+ }
97
+ });
98
+ async function addLanguage(language) {
99
+ var _a;
100
+ if (cache.has(language)) {
101
+ return;
102
+ }
103
+ cache.add(language);
104
+ let loadBackend;
105
+ if ((_a = services.backendConnector) == null ? void 0 : _a.backend) {
106
+ loadBackend = reloadResources([language], [namespace]);
107
+ }
108
+ const loadLazyResources = lazyResources == null ? void 0 : lazyResources[language];
109
+ if (!loadLazyResources) {
110
+ await loadBackend;
111
+ return;
112
+ }
113
+ const [result] = await Promise.allSettled([
114
+ loadLazyResources(),
115
+ loadBackend
116
+ ]);
117
+ if (result.status === "rejected") {
118
+ throw result.reason;
119
+ }
120
+ addResourceBundle(
121
+ language,
122
+ namespace,
123
+ result.value.messages,
124
+ true,
125
+ false
126
+ );
127
+ }
128
+ }
129
+ }
130
+
131
+ export { AppTranslationApiImpl as A };
132
+ //# sourceMappingURL=AppTranslationImpl-8560b672.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppTranslationImpl-8560b672.esm.js","sources":["../../src/apis/implementations/AppTranslationApi/AppTranslationImpl.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\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 AppTranslationApi,\n TranslationRef,\n} from '@backstage/core-plugin-api/alpha';\nimport i18next, { type i18n } from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport LanguageDetector from 'i18next-browser-languagedetector';\n\nimport { TranslationMessages } from '../../../alpha';\n\n/** @alpha */\nexport type ExperimentalI18n = {\n supportedLanguages: string[];\n fallbackLanguage?: string | string[];\n messages?: Array<{\n ref: TranslationRef;\n messages?: TranslationMessages<TranslationRef>;\n lazyMessages: Record<\n string,\n () => Promise<{ messages: TranslationMessages<TranslationRef> }>\n >;\n }>;\n};\n\n/** @alpha */\nexport class AppTranslationApiImpl implements AppTranslationApi {\n static create(options?: ExperimentalI18n) {\n const i18n = i18next.createInstance().use(initReactI18next);\n\n i18n.use(LanguageDetector);\n\n i18n.init({\n fallbackLng: options?.fallbackLanguage || 'en',\n supportedLngs: options?.supportedLanguages || ['en'],\n interpolation: {\n escapeValue: false,\n },\n react: {\n bindI18n: 'loaded languageChanged',\n },\n });\n\n return new AppTranslationApiImpl(i18n, options);\n }\n\n private readonly cache = new WeakSet<TranslationRef>();\n private readonly lazyCache = new WeakMap<TranslationRef, Set<string>>();\n\n getI18n() {\n return this.i18n;\n }\n\n initMessages(options?: ExperimentalI18n) {\n if (options?.messages?.length) {\n options.messages.forEach(appMessage => {\n if (appMessage.messages) {\n this.addResources(appMessage.ref, appMessage.messages);\n }\n\n if (appMessage.lazyMessages) {\n this.addLazyResources(appMessage.ref, appMessage.lazyMessages);\n }\n });\n }\n }\n\n addResourcesByRef<Messages extends Record<string, string>>(\n translationRef: TranslationRef<Messages>,\n ): void {\n this.addResources(translationRef);\n this.addLazyResources(translationRef);\n }\n\n addResources<Messages extends Record<string, string>>(\n translationRef: TranslationRef<Messages>,\n initResources?: TranslationMessages<TranslationRef<Messages>>,\n ) {\n const resources = initResources || translationRef.getResources();\n if (!resources || this.cache.has(translationRef)) {\n return;\n }\n this.cache.add(translationRef);\n Object.entries(resources).forEach(([language, messages]) => {\n this.i18n.addResourceBundle(\n language,\n translationRef.getId(),\n messages,\n true,\n false,\n );\n });\n }\n\n addLazyResources<Messages extends Record<string, string>>(\n translationRef: TranslationRef<Messages>,\n initResources?: Record<\n string,\n () => Promise<{ messages: TranslationMessages<TranslationRef> }>\n >,\n ) {\n let cache = this.lazyCache.get(translationRef);\n\n if (!cache) {\n cache = new Set();\n this.lazyCache.set(translationRef, cache);\n }\n\n const {\n language: currentLanguage,\n services,\n options,\n addResourceBundle,\n reloadResources,\n } = this.i18n;\n\n if (cache.has(currentLanguage)) {\n return;\n }\n\n const namespace = translationRef.getId();\n const lazyResources = initResources || translationRef.getLazyResources();\n\n const fallbackLanguages = services.languageUtils.getFallbackCodes(\n options.fallbackLng,\n currentLanguage,\n ) as string[];\n\n Promise.allSettled(\n [...fallbackLanguages, currentLanguage].map(addLanguage),\n ).then(results => {\n if (results.some(result => result.status === 'fulfilled')) {\n this.i18n.emit('loaded');\n }\n });\n\n async function addLanguage(language: string) {\n if (cache!.has(language)) {\n return;\n }\n\n cache!.add(language);\n\n let loadBackend: Promise<void> | undefined;\n\n if (services.backendConnector?.backend) {\n loadBackend = reloadResources([language], [namespace]);\n }\n\n const loadLazyResources = lazyResources?.[language];\n\n if (!loadLazyResources) {\n await loadBackend;\n return;\n }\n\n const [result] = await Promise.allSettled([\n loadLazyResources(),\n loadBackend,\n ]);\n\n if (result.status === 'rejected') {\n throw result.reason;\n }\n\n addResourceBundle(\n language,\n namespace,\n result.value.messages,\n true,\n false,\n );\n }\n }\n\n private constructor(private readonly i18n: i18n, options?: ExperimentalI18n) {\n this.initMessages(options);\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import * as _backstage_types from '@backstage/types';
6
6
  import { Observable, JsonValue } from '@backstage/types';
7
7
  import { Config, AppConfig } from '@backstage/config';
8
8
  export { ConfigReader } from '@backstage/config';
9
+ import { TranslationRef } from '@backstage/core-plugin-api/alpha';
9
10
 
10
11
  /**
11
12
  * Prop types for the ApiProvider component.
@@ -802,6 +803,13 @@ type TargetRouteMap<ExternalRoutes extends {
802
803
  type AppRouteBinder = <ExternalRoutes extends {
803
804
  [name: string]: ExternalRouteRef;
804
805
  }>(externalRoutes: ExternalRoutes, targetRoutes: PartialKeys<TargetRouteMap<ExternalRoutes>, KeysWithType<ExternalRoutes, ExternalRouteRef<any, true>>>) => void;
806
+ /**
807
+ * TODO: To be remove when TranslationMessages in packages/core-app-api/src/app/TranslationResource.ts
808
+ * come to be public
809
+ *
810
+ * @ignore
811
+ * */
812
+ type TranslationMessages<T> = T extends TranslationRef<infer R> ? Record<string, Partial<R>> : never;
805
813
  /**
806
814
  * The options accepted by {@link createSpecializedApp}.
807
815
  *
@@ -900,6 +908,21 @@ type AppOptions = {
900
908
  bindRoutes?(context: {
901
909
  bind: AppRouteBinder;
902
910
  }): void;
911
+ /**
912
+ * TODO: Change to ExperimentalI18n type when packages/core-app-api/src/apis/implementations/AppTranslationApi/AppTranslationImpl.ts
913
+ * become to public
914
+ */
915
+ __experimentalI18n?: {
916
+ supportedLanguages: string[];
917
+ fallbackLanguage?: string | string[];
918
+ messages?: Array<{
919
+ ref: TranslationRef;
920
+ messages?: TranslationMessages<TranslationRef>;
921
+ lazyMessages: Record<string, () => Promise<{
922
+ messages: TranslationMessages<TranslationRef>;
923
+ }>>;
924
+ }>;
925
+ };
903
926
  };
904
927
  /**
905
928
  * The public API of the output of {@link createSpecializedApp}.
package/dist/index.esm.js CHANGED
@@ -8,7 +8,12 @@ import { ConfigReader } from '@backstage/config';
8
8
  export { ConfigReader } from '@backstage/config';
9
9
  import { createRoutesFromChildren, Route, useLocation, matchRoutes, Routes, generatePath, useRoutes } from 'react-router-dom';
10
10
  import useAsync from 'react-use/lib/useAsync';
11
+ import { appTranslationApiRef } from '@backstage/core-plugin-api/alpha';
11
12
  import useObservable from 'react-use/lib/useObservable';
13
+ import { I18nextProvider } from 'react-i18next';
14
+ import { A as AppTranslationApiImpl } from './esm/AppTranslationImpl-8560b672.esm.js';
15
+ import 'i18next';
16
+ import 'i18next-browser-languagedetector';
12
17
 
13
18
  var __defProp$l = Object.defineProperty;
14
19
  var __defNormalProp$l = (obj, key, value) => key in obj ? __defProp$l(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -3179,6 +3184,12 @@ function resolveRouteBindings(bindRoutes) {
3179
3184
  return result;
3180
3185
  }
3181
3186
 
3187
+ function AppTranslationProvider({ children }) {
3188
+ const appTranslationAPi = useApi(appTranslationApiRef);
3189
+ const i18n = appTranslationAPi.getI18n();
3190
+ return /* @__PURE__ */ React.createElement(I18nextProvider, { i18n }, children);
3191
+ }
3192
+
3182
3193
  var __defProp = Object.defineProperty;
3183
3194
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3184
3195
  var __publicField = (obj, key, value) => {
@@ -3294,6 +3305,7 @@ class AppManager {
3294
3305
  __publicField(this, "configLoader");
3295
3306
  __publicField(this, "defaultApis");
3296
3307
  __publicField(this, "bindRoutes");
3308
+ __publicField(this, "appTranslationApi");
3297
3309
  __publicField(this, "appIdentityProxy", new AppIdentityProxy());
3298
3310
  __publicField(this, "apiFactoryRegistry");
3299
3311
  __privateAdd(this, _getProviderCalled, false);
@@ -3308,6 +3320,9 @@ class AppManager {
3308
3320
  this.defaultApis = (_e = options.defaultApis) != null ? _e : [];
3309
3321
  this.bindRoutes = options.bindRoutes;
3310
3322
  this.apiFactoryRegistry = new ApiFactoryRegistry();
3323
+ this.appTranslationApi = AppTranslationApiImpl.create(
3324
+ options.__experimentalI18n
3325
+ );
3311
3326
  }
3312
3327
  getPlugins() {
3313
3328
  return Array.from(this.plugins);
@@ -3421,7 +3436,7 @@ class AppManager {
3421
3436
  }
3422
3437
  }
3423
3438
  const { ThemeProvider = AppThemeProvider } = this.components;
3424
- return /* @__PURE__ */ React.createElement(ApiProvider, { apis: this.getApiHolder() }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(ThemeProvider, null, /* @__PURE__ */ React.createElement(
3439
+ return /* @__PURE__ */ React.createElement(ApiProvider, { apis: this.getApiHolder() }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(AppTranslationProvider, null, /* @__PURE__ */ React.createElement(ThemeProvider, null, /* @__PURE__ */ React.createElement(
3425
3440
  RoutingProvider,
3426
3441
  {
3427
3442
  routePaths: routing.paths,
@@ -3440,7 +3455,7 @@ class AppManager {
3440
3455
  },
3441
3456
  children
3442
3457
  )
3443
- ))));
3458
+ )))));
3444
3459
  };
3445
3460
  return Provider;
3446
3461
  }
@@ -3484,6 +3499,11 @@ class AppManager {
3484
3499
  deps: {},
3485
3500
  factory: () => this.appIdentityProxy
3486
3501
  });
3502
+ this.apiFactoryRegistry.register("static", {
3503
+ api: appTranslationApiRef,
3504
+ deps: {},
3505
+ factory: () => this.appTranslationApi
3506
+ });
3487
3507
  this.apiFactoryRegistry.register("default", {
3488
3508
  api: featureFlagsApiRef,
3489
3509
  deps: {},