Skip to content

added react-i18next to FE #3682

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

Merged
merged 5 commits into from
Aug 24, 2022
Merged

added react-i18next to FE #3682

merged 5 commits into from
Aug 24, 2022

Conversation

Kira-Pilot
Copy link
Member

resolves #3486

We now have FE translations, which will make handling strings a lot simpler and less error prone. This PR encapsulates the addition of the library, as well as a proof of concept so folks can get rolling quickly.

Translations and translation config is all located in a new directory: src/i18n. Translations can be created and then used in files and tests. I've left some comments below to illustrate usage.

After this PR is merged up, we should cease to use Language objects, and instead should add translation strings for all of our text. Translation strings can be broken out into different namespaces. Namespaces are located in src/i18n/en. After adding a new namespace, be sure to export that namespace module in src/i18n/en/index.ts. Over time, we should try to remove old Language blocks as we come across them in areas we're working on.

Let me know if you have any questions or concerns!

@Kira-Pilot Kira-Pilot requested a review from a team as a code owner August 24, 2022 19:42
@Kira-Pilot Kira-Pilot requested review from code-asher and removed request for a team August 24, 2022 19:42
@@ -57,6 +52,7 @@ export const WorkspaceScheduleButton: React.FC<WorkspaceScheduleButtonProps> = (
deadlineMinusEnabled,
canUpdateWorkspace,
}) => {
const { t } = useTranslation("workspacePage")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the primary way we should access translations in components: by using the useTranslation hook. Here, I have passed the translation hook the namespace I am interested in procuring translations from.

@@ -34,70 +21,72 @@ export const getStatus = (
icon: React.ReactNode
} => {
const status = getWorkspaceStatus(build)
const { t } = i18next
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is another way we can access translations: directly from i18next (please note import above). I am accessing translations this way because I am working within a function, not a component (and you can't use hooks in functions).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the hook preferable when you have the choice?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh is it because it takes the namespace?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely! But also, useTranslation gets called when language changes (i18next.changeLanguage). Importing directly means translations are not updated when language changes. There are workarounds here, and we don't need to worry about it right now because we're not actually translating our app, but it's one thing less to worry down the road if we ever do translate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to know. Can you add something to Notion about this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

switch (status) {
case undefined:
return {
text: StatusLanguage.loading,
text: t("common:workspace_status.loading", { ns: "common" }),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's another way to namespace. There's actually a third way to namespace: I could write
t("common:workspace_status.loading"), but some people find this more difficult to read. I don't have a preference myself.

@@ -171,7 +174,7 @@ describe("WorkspacePage", () => {
await testStatus(MockDeletingWorkspace, DisplayStatusLanguage.deleting)
})
it("shows the Deleted status when the workspace is deleted", async () => {
await testStatus(MockDeletedWorkspace, DisplayStatusLanguage.deleted)
await testStatus(MockDeletedWorkspace, t("workspace_status.deleted", { ns: "common" }))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use our new translations in our tests, too, so our tests don't become brittle.

<Route path={path ?? route} element={<RequireAuth>{ui}</RequireAuth>} />
</Routes>
</ThemeProvider>
</I18nextProvider>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When testing, we must wrap rendered components inside this provider.

Copy link
Member

@code-asher code-asher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me. Do we have a fixed strategy for namespacing? Like common for shared components and pageName for pages?

@Kira-Pilot
Copy link
Member Author

@code-asher Yeah, I generally like a common for language describing entities used across the app. The documentation uses common as an example, as well. The rest of the modules can be broken out according to dev discretion IMO. Page seems like a logical place to start but if a page becomes very complex, string-wise, we can always break it down into submodules. I figure start large and then abstract when we need to.

Copy link
Contributor

@jsjoeio jsjoeio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your PR description and comments made this super easy to review 👏🏼

@@ -3,6 +3,8 @@ import { createRoot } from "react-dom/client"
import { Interpreter } from "xstate"
import { App } from "./app"

import "./i18n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Why do we import this here? Is that to initialize translations or something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, it's part of the setup!

resources,
})
.catch((error) => {
console.error(error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ What will error look like? Would prefixing the error messages with something like [Translation Service] help us distinguish these vs. others?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add that! But really, I added this catch block to get around a linting error: no-floating-promises
It's not really adding a lot beside quieting lint AFAIK.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh we are going to add that to code-server! I think it can be super helpful in insuring you don't accidentally forget to await a promise. Example: ideally it would catch issues like this one

@@ -0,0 +1 @@
export * from "./i18n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

barrel file FTW 🚀

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I figure this is a good place for one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add react-i18next to the FE
4 participants