Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .changeset/i18n-web-translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@baseapp-frontend/components": minor
"@baseapp-frontend/design-system": minor
"@baseapp-frontend/utils": minor
"@baseapp-frontend/authentication": minor
"@baseapp-frontend/i18n": minor
---

Translate every web string to English, Spanish and Portuguese through react-intl.

- `components` and `design-system` web UI render all text via `formatMessage` with ids
namespaced per module; catalogs ship in `@baseapp-frontend/{components,design-system}/locales`.
Native code and shared `common/` code used by native are unchanged.
- Default prop strings (e.g. `cancelText`, `placeholder`, `title`) are now translated fallbacks;
passing the prop still overrides them.
- `utils`: `getZodMessages(intl)`, `ZOD_MESSAGE_DESCRIPTORS` and the `IntlFormatter` type.
- `authentication`: intl factories next to every validation schema
(`getLoginValidationSchema`, `getSignUpValidationSchema`, `getCodeValidationSchema`, …) and a
new `mfaFormOptions` option on `useLogin`. Existing schema constants are unchanged.
- `i18n`: `IntlProviderWrapper` accepts `additionalMessagesByLocale`; the app-specific
`settings.*` messages moved out of the package catalog.
- Web components that render text now require an `IntlProvider` ancestor (the package test
providers include one).
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import { PASSWORD_REGEX, ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
PASSWORD_REGEX,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

import type { ChangePasswordForm } from './types'

export const DEFAULT_VALIDATION_SCHEMA = z
.object({
currentPassword: z.string().nonempty(ZOD_MESSAGE.required),
newPassword: z.string().min(1, ZOD_MESSAGE.required).regex(PASSWORD_REGEX, {
message: ZOD_MESSAGE.password,
}),
confirmNewPassword: z.string().nonempty(ZOD_MESSAGE.required),
})
.refine(({ confirmNewPassword, newPassword }) => newPassword === confirmNewPassword, {
message: ZOD_MESSAGE.passwordDoNotMatch,
path: ['confirmNewPassword'],
})
const createValidationSchema = (messages: ZodMessages) =>
z
.object({
currentPassword: z.string().nonempty(messages.required),
newPassword: z.string().min(1, messages.required).regex(PASSWORD_REGEX, {
message: messages.password,
}),
confirmNewPassword: z.string().nonempty(messages.required),
})
.refine(({ confirmNewPassword, newPassword }) => newPassword === confirmNewPassword, {
message: messages.passwordDoNotMatch,
path: ['confirmNewPassword'],
})

export const DEFAULT_VALIDATION_SCHEMA = createValidationSchema(ZOD_MESSAGE)

export const getChangePasswordValidationSchema = (intl: IntlFormatter) =>
createValidationSchema(getZodMessages(intl))

export const DEFAULT_INITIAL_VALUES: ChangePasswordForm = {
currentPassword: '',
Expand Down
21 changes: 16 additions & 5 deletions packages/authentication/modules/access/useLogin/constants.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

import type { LoginRequest } from '../../../types/auth'

export const DEFAULT_VALIDATION_SCHEMA = z.object({
email: z.string().min(1, ZOD_MESSAGE.required).email(ZOD_MESSAGE.email),
password: z.string().min(1, ZOD_MESSAGE.required),
})
const createValidationSchema = (messages: ZodMessages) =>
z.object({
email: z.string().min(1, messages.required).email(messages.email),
password: z.string().min(1, messages.required),
})

export const DEFAULT_VALIDATION_SCHEMA = createValidationSchema(ZOD_MESSAGE)

export const getLoginValidationSchema = (intl: IntlFormatter) =>
createValidationSchema(getZodMessages(intl))

export const DEFAULT_INITIAL_VALUES: LoginRequest = {
email: '',
Expand Down
5 changes: 4 additions & 1 deletion packages/authentication/modules/access/useLogin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
LoginJWTResponse,
LoginMfaRequest,
} from '../../../types/auth'
import type { MfaRequest } from '../../../types/mfa'
import { User } from '../../../types/user'
import {
isLoginChangeExpiredPasswordRedirectResponse,
Expand All @@ -35,6 +36,7 @@ import type { ApiClass, LoginParams, UseLoginOptions } from './types'

const useLogin = <TApiClass extends ApiClass = typeof AuthApi>({
loginFormOptions = {},
mfaFormOptions = {},
loginOptions = {},
mfaOptions = {},
accessKeyName = ACCESS_KEY_NAME,
Expand Down Expand Up @@ -114,10 +116,11 @@ const useLogin = <TApiClass extends ApiClass = typeof AuthApi>({
},
})

const mfaForm = useForm({
const mfaForm = useForm<Pick<MfaRequest, 'code'>>({
defaultValues: CODE_VALIDATION_INITIAL_VALUES,
resolver: zodResolver(CODE_VALIDATION_SCHEMA),
mode: 'onBlur',
...mfaFormOptions,
})

const mfaMutation = useMutation({
Expand Down
7 changes: 5 additions & 2 deletions packages/authentication/modules/access/useLogin/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { UseFormProps } from 'react-hook-form'

import AuthApi from '../../../services/auth'
import type { CustomJWTKeyNames, LoginMfaRequest, LoginResponse } from '../../../types/auth'
import type { MfaRequest } from '../../../types/mfa'

export type ApiClass = {
login: (...args: any[]) => Promise<any>
Expand All @@ -11,9 +12,11 @@ export type ApiClass = {
export type LoginParams<T extends ApiClass> = Parameters<T['login']>[0]
export type LoginReturn<T extends ApiClass> = Awaited<ReturnType<T['login']>>

export interface UseLoginOptions<TApiClass extends ApiClass = typeof AuthApi>
extends CustomJWTKeyNames {
export interface UseLoginOptions<
TApiClass extends ApiClass = typeof AuthApi,
> extends CustomJWTKeyNames {
loginFormOptions?: UseFormProps<LoginParams<TApiClass>>
mfaFormOptions?: UseFormProps<Pick<MfaRequest, 'code'>>
loginOptions?: UseMutationOptions<LoginReturn<TApiClass>, unknown, LoginParams<TApiClass>, any>
mfaOptions?: UseMutationOptions<LoginResponse, unknown, LoginMfaRequest, any>
ApiClass?: TApiClass
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

import type { ForgotPasswordRequest } from '../../../types/auth'

export const DEFAULT_VALIDATION_SCHEMA = z.object({
email: z.string().min(1, ZOD_MESSAGE.required).email(ZOD_MESSAGE.email),
})
const createValidationSchema = (messages: ZodMessages) =>
z.object({
email: z.string().min(1, messages.required).email(messages.email),
})

export const DEFAULT_VALIDATION_SCHEMA = createValidationSchema(ZOD_MESSAGE)

export const getRecoverPasswordValidationSchema = (intl: IntlFormatter) =>
createValidationSchema(getZodMessages(intl))

export const DEFAULT_INITIAL_VALUES: ForgotPasswordRequest = {
email: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

import type { RequestEmailChangeRequest } from './types'

export const DEFAULT_VALIDATION_SCHEMA = z.object({
newEmail: z.string().email(ZOD_MESSAGE.email).min(1, ZOD_MESSAGE.required),
})
const createValidationSchema = (messages: ZodMessages) =>
z.object({
newEmail: z.string().email(messages.email).min(1, messages.required),
})

export const DEFAULT_VALIDATION_SCHEMA = createValidationSchema(ZOD_MESSAGE)

export const getRequestEmailChangeValidationSchema = (intl: IntlFormatter) =>
createValidationSchema(getZodMessages(intl))

export const DEFAULT_INITIAL_VALUES: RequestEmailChangeRequest = {
newEmail: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import { PASSWORD_REGEX, ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
PASSWORD_REGEX,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

import type { ResetPasswordForm } from './types'

export const DEFAULT_VALIDATION_SCHEMA = z
.object({
newPassword: z.string().min(1, ZOD_MESSAGE.required).regex(PASSWORD_REGEX, {
message: ZOD_MESSAGE.password,
}),
confirmNewPassword: z.string().nonempty(ZOD_MESSAGE.required),
})
.refine(({ confirmNewPassword, newPassword }) => newPassword === confirmNewPassword, {
message: ZOD_MESSAGE.passwordDoNotMatch,
path: ['confirmNewPassword'],
})
const createValidationSchema = (messages: ZodMessages) =>
z
.object({
newPassword: z.string().min(1, messages.required).regex(PASSWORD_REGEX, {
message: messages.password,
}),
confirmNewPassword: z.string().nonempty(messages.required),
})
.refine(({ confirmNewPassword, newPassword }) => newPassword === confirmNewPassword, {
message: messages.passwordDoNotMatch,
path: ['confirmNewPassword'],
})

export const DEFAULT_VALIDATION_SCHEMA = createValidationSchema(ZOD_MESSAGE)

export const getResetPasswordValidationSchema = (intl: IntlFormatter) =>
createValidationSchema(getZodMessages(intl))

export const DEFAULT_INITIAL_VALUES: ResetPasswordForm = {
newPassword: '',
Expand Down
56 changes: 39 additions & 17 deletions packages/authentication/modules/access/useSignUp/constants.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
import { PASSWORD_REGEX, ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
PASSWORD_REGEX,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

import type { RegisterRequest } from '../../../types/auth'

export const DEFAULT_VALIDATION_SCHEMA_WITH_NAME = z.object({
name: z.string().min(1, ZOD_MESSAGE.required),
password: z.string().min(1, ZOD_MESSAGE.required).regex(PASSWORD_REGEX, {
message: ZOD_MESSAGE.password,
}),
email: z.string().min(1, ZOD_MESSAGE.required).email(ZOD_MESSAGE.email),
})

export const DEFAULT_VALIDATION_SCHEMA = z.object({
firstName: z.string().min(1, ZOD_MESSAGE.required),
lastName: z.string().min(1, ZOD_MESSAGE.required),
password: z.string().min(1, ZOD_MESSAGE.required).regex(PASSWORD_REGEX, {
message: ZOD_MESSAGE.password,
}),
email: z.string().min(1, ZOD_MESSAGE.required).email(ZOD_MESSAGE.email),
})
const createPasswordSchema = (messages: ZodMessages) =>
z.string().min(1, messages.required).regex(PASSWORD_REGEX, {
message: messages.password,
})

const createEmailSchema = (messages: ZodMessages) =>
z.string().min(1, messages.required).email(messages.email)

const createValidationSchemaWithName = (messages: ZodMessages) =>
z.object({
name: z.string().min(1, messages.required),
password: createPasswordSchema(messages),
email: createEmailSchema(messages),
})

const createValidationSchema = (messages: ZodMessages) =>
z.object({
firstName: z.string().min(1, messages.required),
lastName: z.string().min(1, messages.required),
password: createPasswordSchema(messages),
email: createEmailSchema(messages),
})

export const DEFAULT_VALIDATION_SCHEMA_WITH_NAME = createValidationSchemaWithName(ZOD_MESSAGE)

export const DEFAULT_VALIDATION_SCHEMA = createValidationSchema(ZOD_MESSAGE)

export const getSignUpValidationSchemaWithName = (intl: IntlFormatter) =>
createValidationSchemaWithName(getZodMessages(intl))

export const getSignUpValidationSchema = (intl: IntlFormatter) =>
createValidationSchema(getZodMessages(intl))

export const DEFAULT_INITIAL_VALUES: RegisterRequest = {
name: '',
Expand Down
19 changes: 15 additions & 4 deletions packages/authentication/modules/mfa/constants.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { ZOD_MESSAGE } from '@baseapp-frontend/utils'
import {
type IntlFormatter,
ZOD_MESSAGE,
type ZodMessages,
getZodMessages,
} from '@baseapp-frontend/utils'

import { z } from 'zod'

export const CODE_VALIDATION_SCHEMA = z.object({
code: z.string().min(1, ZOD_MESSAGE.required),
})
const createCodeValidationSchema = (messages: ZodMessages) =>
z.object({
code: z.string().min(1, messages.required),
})

export const CODE_VALIDATION_SCHEMA = createCodeValidationSchema(ZOD_MESSAGE)

export const getCodeValidationSchema = (intl: IntlFormatter) =>
createCodeValidationSchema(getZodMessages(intl))

export const CODE_VALIDATION_INITIAL_VALUES = {
code: '',
Expand Down
24 changes: 23 additions & 1 deletion packages/components/locales/en.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
{
"comments.actions.delete": "Delete Comment",
"comments.actions.edit": "Edit Comment",
"comments.actions.pin": "Pin Comment",
"comments.actions.share": "Share Comment",
"comments.actions.unpin": "Unpin Comment",
"comments.create.placeholder": "Comment...",
"comments.create.submit.ariaLabel": "create comment",
"comments.item.actionsOverlay.title": "Comment",
"comments.item.avatar.alt": "Comment's user avatar",
"comments.item.deletedUser": "Deleted User",
"comments.loading.more": "loading more comments",
"comments.pinned.badge": "Pinned",
"comments.reaction.ariaLabel": "react to comment {commentId}",
"comments.reaction.count.ariaLabel": "reactions count {commentId}",
"comments.replies.showMore": "Show more replies ({count})",
"comments.reply.ariaLabel": "reply to comment {commentId}",
"comments.reply.count.ariaLabel": "replies count {commentId}"
"comments.reply.count.ariaLabel": "replies count {commentId}",
"comments.upsertActions.attachFiles.ariaLabel": "Attach files",
"comments.upsertActions.mention.ariaLabel": "mention",
"shared.actionsOverlay.ariaLabel": "actions overlay",
"shared.actionsOverlay.defaultTitle": "Item",
"shared.actionsOverlay.deleteDialog.content": "Are you sure you want to delete? This action cannot be undone.",
"shared.actionsOverlay.deleteDialog.title": "Delete {title}?",
"shared.actionsOverlay.deleteItem.ariaLabel": "delete item",
"shared.actionsOverlay.showMenuOptions.ariaLabel": "Show menu options",
"shared.searchNotFound.message": "Check your spelling or try another search.",
"shared.searchNotFound.title": "No results found",
"shared.socialInput.placeholder": "Message...",
"shared.socialInput.submit.ariaLabel": "submit actions",
"shared.timestamp.today": "Today",
"shared.updateSubmitActions.cancel.ariaLabel": "cancel comment edit",
"shared.updateSubmitActions.save.ariaLabel": "save comment edit"
}
Loading
Loading