-
Notifications
You must be signed in to change notification settings - Fork 6.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] i18n feature #4454
Comments
Just for awareness, Backstage's membership in CNCF may help: Not suggesting this could "do the translation for us", but resources experienced in that area might have guidance/suggestions as well. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Hey @freben, any news about that? I could help to make that happening |
No news yet. I can say though, that there's one more angle to this that makes i18n interesting: I'm getting more and more interest from people that want to replace individual little text snippets inside their Backstage instance, not for translation purposes, but for personalization / branding purposes. Sometimes we've added a prop or similar to make them customizable, but an i18n framework properly used will probably greatly help with things like that as well. Any movement or suggestion is welcome. Maybe even starting with just pointing to commonly used libraries and their strengths/drawbacks |
@freben two that I would suggest is https://www.npmjs.com/package/typesafe-i18n https://www.npmjs.com/package/i18next I see pluses and minuses in both directions. i18next just seems like a little better choice as a lot of adopters could leverage their existing tools and processes around localization. |
https://github.com/sibelius/ast-i18n This also looks like an interesting project which could do a fair number of the initial heavy lifting maybe 🤷♂️ Might be worth a pass and a look? |
@webark yep looks interesting! At a first glance I'm a bit worried about seemingly generated message IDs, it'll make API stability a bit troublesome |
yea. I figured it would be interesting to see what it spit out and if it was helpful |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
We in the Backstage community in Brazil (in general) are very interested in this. 🇧🇷 |
Status: Open for comments My intention was to design a general ideia to provide a first step to use i18n in Backstage. NeedBackstage is reaching a big audience and not everyone knows english, so the developers maintainers are in necessity for translate the product to provide a better experience. Also some maintainers want to change some texts in the front-end but not every plugin (or component) has props for that The ideia is add i18next and support a possibility to translate your plugin. ProposalThe fist step is provide a simple integration for each plugin add tr First we will update the import {Resource} from "i18next";
export type PluginConfig<
Routes extends AnyRoutes,
ExternalRoutes extends AnyExternalRoutes,
PluginInputOptions extends {},
> = {
id: string;
apis?: Iterable<AnyApiFactory>;
routes?: Routes;
externalRoutes?: ExternalRoutes;
featureFlags?: PluginFeatureFlagConfig[];
__experimentalConfigure?(options?: PluginInputOptions): {};
locale?: Resource
}; This will provide a simple way from the plugin export the keys used inside ( we can provide definitions for that too). Example: export constcatalogPlugin= createPlugin({
id: 'catalog',
apis: [
createApiFactory({
api:catalogApiRef,
deps: {
discoveryApi:discoveryApiRef,
fetchApi:fetchApiRef,
},
factory: ({ discoveryApi, fetchApi }) =>
new CatalogClient({ discoveryApi, fetchApi }),
}),
createApiFactory({
api:starredEntitiesApiRef,
deps: { storageApi:storageApiRef},
factory: ({ storageApi }) =>
new DefaultStarredEntitiesApi({ storageApi }),
}),
],
routes: {
catalogIndex:rootRouteRef,
catalogEntity:entityRouteRef,
},
externalRoutes: {
createComponent:createComponentRouteRef,
viewTechDoc:viewTechDocRouteRef,
},
__experimentalConfigure(
options?: CatalogInputPluginOptions,
): CatalogPluginOptions {
const defaultOptions = {
createButtonTitle: 'Create',
};
return { ...defaultOptions, ...options };
},
locale: {
en: {
name: "catalog",
description: "All your software catalog entities"
}
}
}); And could be used like that in the component: import React from 'react';
import { useTranslation } from 'react-i18next';
export function MyComponent() {
const { t } = useTranslation(["catalog"])
return <p>{t('name')}</p>
} important: Each component will be using a namespace based in the plugin id open question: How provide a better devexp here? because in this way the developer needs to remember to use the namespace all the times With this setup, the backstage could load locale too and override those keys const app = createApp({
localeConfig: {
lng: "pt-br",
supportedLngs: ['pt', 'en']
resources: {
pt: {
app: {
screeName: "Minha tela",
},
catalog: {
name: "awesome catalog"
}
}
}
},
apis,
plugins: Object.values(plugins),
icons: {
// Custom icon example
alert: AlarmIcon,
},
components: {
SignInPage: props => {
return (
<SignInPage
{...props}
providers={['guest', 'custom', ...providers]}
title="Select a sign-in method"
align="center"
/>
);
},
},
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(apiDocsPlugin.externalRoutes, {
registerApi: catalogImportPlugin.routes.importPage,
});
bind(scaffolderPlugin.externalRoutes, {
registerComponent: catalogImportPlugin.routes.importPage,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(orgPlugin.externalRoutes, {
catalogIndex: catalogPlugin.routes.catalogIndex,
});
},
}); Important The language → plugin (namespace) → keys You don’t need to provide all the keys, the You can add new languages too to match your expectation over the changes in your app. The keys you want to use in your
AlternativesLoad from BackendOne interesting alternative is use the Backend system to load all translations. This could be nice to provide a simple way to change locales and a fast update system (like publish the core locales in a CDN) I didn’t go deep in this approach but we can talk more about that Use a differrent libraryWe can choose a different library (like https://github.com/ivanhofer/typesafe-i18n), but I think this will change only details about the implementation, not the whole approach (plugins are locale owner’ s). My ideia is use RisksHow I know this plugin use i18n?A pain point is how give this information for the developer. The experience could be weird when I have 10 plugins in my language and just one small part only in English. We can enforce translation, but this could raise the contributing entrypoint Static loadThis solution is very simple and a start point. Because of that, the locale is almost all loadead when the We have some options to add or load translations we could explore Backend translationThis solution don’t have any use case to translate messages from the backend. The experience could be weird when your interface is in a language and you receive some messages in English Locales keys coupled with plugin releaseIn the current proposal, the keys values are really coupled with the plugin release. In some cases, to add a new language for the plugin, I will need to release a new version every time. We can isolate translations in a new module, something like this: plugins/i18n-translations/src/catalog.ts
export default {
en: {
name: "catalog",
description: "All your software catalog entities"
},
pt: {
name: "catalog",
description: "Todas as suas entidades de catálogo de software"
}
} And use in each plugin: import { catalogLocale } from '@backstage/plugin-i18n-translations'
export const catalogPlugin = createPlugin({
id: 'catalog',
// some code
locale: catalogLocale
}); This approach could be nice to move all translations for one place, and we can add some utilities to help in the plugin development (Like a simple way to resolve the namespace for the plugin id) Add complexity, in particular regarding plugin developmentUsing i18n is a new thing to be aware when creating a plugin. Over the time, this could bring a high entrypoint to new contributing to Backstage. Over the time we can simplify this flow, using tools to give a better experience in this process @Rugvip @freben please take a look in this first ideia, so we can move foward this RFC ;) |
Excellent proposal @angeliski! |
@angeliski awesome stuff indeed 😁 I think it's well worth moving forward. I think it'd be best if you move your proposal to a separate RFC tbh, and focus the discussion there on the implementation. That could be a draft PRFC too if it ends up making sense. There are a couple of requirements that I'd like to add in to be considered as part of this: Must haves
Nice to have
|
Nice @Rugvip |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
@angeliski Do we had another RFC link. I want to know the newest solutions about i18n |
Hi! Just wanted to keep an eye on this RFC, also we did some first few steps to this direction in the Playlist plugin |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Also interested in this; Canada has two official languages so using Backstage in gov would be improved with i18n. This will also reach into the YAML for us since our services usually have two names and two acronyms. # https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
name: SelfServicePortal
labels:
name-fr: Portail libre-service
acronym-en: SSP
acronym-fr: PLS
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: SelfServicePortal-DEV
links:
- title: Home page
url: https://myhost/ssp-pls/en/
icon: Home
type: home-en
- title: Page d'accueil
url: https:/myhost/ssp-pls/fr/
icon: Home
type: home-fr
spec:
type: website
lifecycle: experimental
owner: guests
system: SelfServicePortal
providesApis: [SSP-API] Some thoughts:
noticing now that this approach will need some refinement since labels will also complain about diacritics if they don't even support spaces. Idk but it wouldn't surprise me if this was a k8s limitation as well. Maybe in kubernetes 2 we will finally be able to have emojis as our app selectors.
Personally, the only constraint on a text field should be the length. Any system that forces me to Maybe our editors should have a shortcut to generate a GUID instead of relying on auto generating one later. This could get messy with references between entities being a little more opaque, but editor hints are nothing new and could be used to give more context to GUIDs everywhere Pretend that the comments were editor hints apiVersion: backstage.io/v1alpha1
kind: System
metadata:
uid: a0c049cb-8a9d-4e1f-bb65-37092cacd100
labels:
name-en: Self Service Portal
name-fr: Portail libre-service ééééééééééé
acronym-en: SSP
acronym-fr: PLS
spec:
owner: group:175dc814-a284-423f-b2c1-9eb238369d91 # labels.name-en: Self Service Portal Owners
---
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
uid: 801347e3-ad45-41a4-bd9a-9e19bc90bbe3
labels:
name-en: SelfServicePortal-DEV
links:
- title: Home page
url: https://myhost/ssp-pls/en/
icon: Home
type: home-en
- title: Page d'accueil
url: https://myhost/ssp-pls/fr/
icon: Home
type: home-fr
spec:
type: website
lifecycle: experimental
owner: group:8865f809-021b-4fa6-bb36-c5d2048c4076 # labels.name-en: guests
system: a0c049cb-8a9d-4e1f-bb65-37092cacd100 # labels.name-en: Self Service Portal
providesApis:
- 3e0523c7-e60c-41dd-8ba6-561f7679f579 # labels.name-en: Self Service Portal API Intellisense could auto suggest the guid to tab-replace as you type the name for a thing you want to reference. Backstage as a language server to provide the intellisense + cross-repo reference validation? I think I've gotten off track, but hopefully this has been a helpful perspective. |
Actually, in some provinces (like Quebec) it would be a hard requirement to support both French and English. |
Another aspect relative to i18n is also the support for all kind of characters : this is currenlty not the case for tags. |
@Kris-B That's kinda different though. The catalog has adjustable validation rules internally where you control what characters are allowed to go into each field of an entity. So you should be able already to do what you need there, specifically in terms of getting those labels into the catalog storage. Then, as a separate concern, maybe you want to localize the presentation of those labels in the browser? That, indeed, could be targeted by this i18n feature. |
@rbideau any issues with this being closed now that the initial implementation (#17436) for this is in place and will be releases as part of the Many thanks to all those who helped on this, especially @mario-mui! 🚀 |
Hi @awanlin, I don't use Backstage anymore but I'm glad the project is still active and that the i18n feature has come to fruition. Feel free to close the issue 👍 |
Yep this work is steaming ahead - closing this RFC! |
Status: Open for comments
Need
This issue is here to start the discussion about adding i18n support in backstage and see if there is enough interest for it.
Proposal
At first, the goal is to gather interest, collect feedback and then start fleshing out the requirement for an i18n feature.
If there is enough interest, we will need to :
Alternatives
NA
Risks
The text was updated successfully, but these errors were encountered: