From 590dd7d85850929f236a5468ea1868b340d7f126 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Wed, 28 Jul 2021 01:13:51 +0800 Subject: [PATCH] feat(phrases): add package and refactor error code --- .github/workflows/phrases-main.yml | 40 +++++++++++++++++ packages/core/package.json | 2 + .../RequestError/collection/guard-errors.ts | 7 --- .../RequestError/collection/oidc-errors.ts | 7 --- .../collection/register-errors.ts | 7 --- .../RequestError/collection/sign-in-errors.ts | 11 ----- .../RequestError/collection/swagger-errors.ts | 7 --- .../core/src/errors/RequestError/index.ts | 12 +++--- .../core/src/errors/RequestError/message.ts | 14 ------ .../core/src/errors/RequestError/types.ts | 17 +------- packages/core/src/index.ts | 4 +- packages/core/src/init/{index.ts => app.ts} | 0 packages/core/src/init/i18n.ts | 9 ++++ packages/core/src/middleware/koa-guard.ts | 4 +- packages/core/src/routes/register.ts | 4 +- packages/core/src/routes/sign-in.ts | 14 +++--- packages/core/src/utils/zod.ts | 4 +- packages/phrases/README.md | 11 +++++ packages/phrases/package.json | 43 +++++++++++++++++++ packages/phrases/src/index.ts | 12 ++++++ packages/phrases/src/locales/en.ts | 39 +++++++++++++++++ packages/phrases/src/locales/zh-cn.ts | 41 ++++++++++++++++++ packages/phrases/src/types.ts | 29 +++++++++++++ packages/phrases/tsconfig.json | 8 ++++ packages/ui/package.json | 1 + packages/ui/src/components/TextLink/index.tsx | 4 +- packages/ui/src/include.d/react-i18next.d.ts | 2 +- packages/ui/src/init/i18n.ts | 8 +--- packages/ui/src/locales/en.json | 10 ----- packages/ui/src/locales/zh-CN.json | 10 ----- packages/ui/src/pages/SignIn/index.tsx | 4 +- yarn.lock | 4 +- 32 files changed, 269 insertions(+), 120 deletions(-) create mode 100644 .github/workflows/phrases-main.yml delete mode 100644 packages/core/src/errors/RequestError/collection/guard-errors.ts delete mode 100644 packages/core/src/errors/RequestError/collection/oidc-errors.ts delete mode 100644 packages/core/src/errors/RequestError/collection/register-errors.ts delete mode 100644 packages/core/src/errors/RequestError/collection/sign-in-errors.ts delete mode 100644 packages/core/src/errors/RequestError/collection/swagger-errors.ts delete mode 100644 packages/core/src/errors/RequestError/message.ts rename packages/core/src/init/{index.ts => app.ts} (100%) create mode 100644 packages/core/src/init/i18n.ts create mode 100644 packages/phrases/README.md create mode 100644 packages/phrases/package.json create mode 100644 packages/phrases/src/index.ts create mode 100644 packages/phrases/src/locales/en.ts create mode 100644 packages/phrases/src/locales/zh-cn.ts create mode 100644 packages/phrases/src/types.ts create mode 100644 packages/phrases/tsconfig.json delete mode 100644 packages/ui/src/locales/en.json delete mode 100644 packages/ui/src/locales/zh-CN.json diff --git a/.github/workflows/phrases-main.yml b/.github/workflows/phrases-main.yml new file mode 100644 index 000000000..6f26ce1dd --- /dev/null +++ b/.github/workflows/phrases-main.yml @@ -0,0 +1,40 @@ +name: Phrases + +on: + push: + branches: [ master ] + paths: [ 'packages/phrases/**' ] + pull_request: + branches: [ master ] + paths: [ 'packages/phrases/**' ] + +jobs: + main: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + # https://github.com/actions/cache/blob/main/examples.md#node---yarn + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install packages + run: yarn + + - name: Lint + working-directory: packages/phrases + run: yarn lint + + - name: Build + working-directory: packages/phrases + run: yarn build diff --git a/packages/core/package.json b/packages/core/package.json index d79697b5c..5192eabab 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -14,12 +14,14 @@ }, "dependencies": { "@logto/essentials": "^1.1.0-rc.1", + "@logto/phrases": "^0.1.0", "@logto/schemas": "^0.1.0", "dayjs": "^1.10.5", "decamelize": "^5.0.0", "dotenv": "^10.0.0", "formidable": "^1.2.2", "got": "^11.8.2", + "i18next": "^20.3.5", "koa": "^2.13.1", "koa-body": "^4.2.0", "koa-logger": "^3.2.1", diff --git a/packages/core/src/errors/RequestError/collection/guard-errors.ts b/packages/core/src/errors/RequestError/collection/guard-errors.ts deleted file mode 100644 index e14bc7e6f..000000000 --- a/packages/core/src/errors/RequestError/collection/guard-errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum GuardErrorCode { - InvalidInput = 'guard.invalid_input', -} - -export const guardErrorMessage: Record = { - [GuardErrorCode.InvalidInput]: 'The request input is invalid.', -}; diff --git a/packages/core/src/errors/RequestError/collection/oidc-errors.ts b/packages/core/src/errors/RequestError/collection/oidc-errors.ts deleted file mode 100644 index b16857e60..000000000 --- a/packages/core/src/errors/RequestError/collection/oidc-errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum OidcErrorCode { - Aborted = 'oidc.aborted', -} - -export const oidcErrorMessage: Record = { - [OidcErrorCode.Aborted]: 'The end-user aborted interaction.', -}; diff --git a/packages/core/src/errors/RequestError/collection/register-errors.ts b/packages/core/src/errors/RequestError/collection/register-errors.ts deleted file mode 100644 index b4ec640ba..000000000 --- a/packages/core/src/errors/RequestError/collection/register-errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum RegisterErrorCode { - UsernameExists = 'register.username_exists', -} - -export const registerErrorMessage: Record = { - [RegisterErrorCode.UsernameExists]: 'The username already exists.', -}; diff --git a/packages/core/src/errors/RequestError/collection/sign-in-errors.ts b/packages/core/src/errors/RequestError/collection/sign-in-errors.ts deleted file mode 100644 index d02b2c77d..000000000 --- a/packages/core/src/errors/RequestError/collection/sign-in-errors.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum SignInErrorCode { - InvalidCredentials = 'sign_in.invalid_credentials', - InvalidSignInMethod = 'sign_in.invalid_sign_in_method', - InsufficientInfo = 'sign_in.insufficient_info', -} - -export const signInErrorMessage: Record = { - [SignInErrorCode.InvalidCredentials]: 'Invalid credentials. Please check your input.', - [SignInErrorCode.InvalidSignInMethod]: 'Current sign-in method is not available.', - [SignInErrorCode.InsufficientInfo]: 'Insufficent sign-in info.', -}; diff --git a/packages/core/src/errors/RequestError/collection/swagger-errors.ts b/packages/core/src/errors/RequestError/collection/swagger-errors.ts deleted file mode 100644 index ace565af7..000000000 --- a/packages/core/src/errors/RequestError/collection/swagger-errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum SwaggerErrorCode { - InvalidZodType = 'swagger.invalid_zod_type', -} - -export const swaggerErrorMessage: Record = { - [SwaggerErrorCode.InvalidZodType]: 'Invalid Zod type, please check route guard config.', -}; diff --git a/packages/core/src/errors/RequestError/index.ts b/packages/core/src/errors/RequestError/index.ts index ee43fa0b3..df5a8ff05 100644 --- a/packages/core/src/errors/RequestError/index.ts +++ b/packages/core/src/errors/RequestError/index.ts @@ -1,19 +1,19 @@ import pick from 'lodash.pick'; -import { requestErrorMessage } from './message'; -import { RequestErrorBody, RequestErrorCode, RequestErrorMetadata } from './types'; +import i18next from 'i18next'; +import { LogtoErrorCode } from '@logto/phrases'; +import { RequestErrorBody, RequestErrorMetadata } from './types'; export * from './types'; -export * from './message'; export default class RequestError extends Error { - code: RequestErrorCode; + code: LogtoErrorCode; status: number; expose: boolean; data: unknown; - constructor(input: RequestErrorMetadata | RequestErrorCode, data?: unknown) { + constructor(input: RequestErrorMetadata | LogtoErrorCode, data?: unknown) { const { code, status = 400 } = typeof input === 'string' ? { code: input } : input; - const message = requestErrorMessage[code]; + const message = i18next.t(code); super(message); diff --git a/packages/core/src/errors/RequestError/message.ts b/packages/core/src/errors/RequestError/message.ts deleted file mode 100644 index 6bff81736..000000000 --- a/packages/core/src/errors/RequestError/message.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { RequestErrorCode } from './types'; -import { guardErrorMessage } from './collection/guard-errors'; -import { oidcErrorMessage } from './collection/oidc-errors'; -import { registerErrorMessage } from './collection/register-errors'; -import { swaggerErrorMessage } from './collection/swagger-errors'; -import { signInErrorMessage } from './collection/sign-in-errors'; - -export const requestErrorMessage: Record = { - ...guardErrorMessage, - ...oidcErrorMessage, - ...registerErrorMessage, - ...swaggerErrorMessage, - ...signInErrorMessage, -}; diff --git a/packages/core/src/errors/RequestError/types.ts b/packages/core/src/errors/RequestError/types.ts index e4a686e97..147583635 100644 --- a/packages/core/src/errors/RequestError/types.ts +++ b/packages/core/src/errors/RequestError/types.ts @@ -1,20 +1,7 @@ -import { GuardErrorCode } from './collection/guard-errors'; -import { OidcErrorCode } from './collection/oidc-errors'; -import { RegisterErrorCode } from './collection/register-errors'; -import { SwaggerErrorCode } from './collection/swagger-errors'; -import { SignInErrorCode } from './collection/sign-in-errors'; - -export { GuardErrorCode, OidcErrorCode, SwaggerErrorCode, RegisterErrorCode, SignInErrorCode }; - -export type RequestErrorCode = - | GuardErrorCode - | OidcErrorCode - | RegisterErrorCode - | SwaggerErrorCode - | SignInErrorCode; +import { LogtoErrorCode } from '@logto/phrases'; export type RequestErrorMetadata = { - code: RequestErrorCode; + code: LogtoErrorCode; status?: number; }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ab3b8a675..c258d1f43 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,12 +6,14 @@ import dotenv from 'dotenv'; dotenv.config(); import Koa from 'koa'; -import initApp from './init'; +import initI18n from './init/i18n'; +import initApp from './init/app'; const app = new Koa(); (async () => { try { + await initI18n(); await initApp(app); } catch (error: unknown) { console.log('Error while initializing app', error); diff --git a/packages/core/src/init/index.ts b/packages/core/src/init/app.ts similarity index 100% rename from packages/core/src/init/index.ts rename to packages/core/src/init/app.ts diff --git a/packages/core/src/init/i18n.ts b/packages/core/src/init/i18n.ts new file mode 100644 index 000000000..6e569157b --- /dev/null +++ b/packages/core/src/init/i18n.ts @@ -0,0 +1,9 @@ +import i18next from 'i18next'; +import resources from '@logto/phrases'; + +export default async function initI18n() { + await i18next.init({ + lng: 'en', + resources, + }); +} diff --git a/packages/core/src/middleware/koa-guard.ts b/packages/core/src/middleware/koa-guard.ts index 51793fa13..19ad380d8 100644 --- a/packages/core/src/middleware/koa-guard.ts +++ b/packages/core/src/middleware/koa-guard.ts @@ -1,4 +1,4 @@ -import RequestError, { GuardErrorCode } from '@/errors/RequestError'; +import RequestError from '@/errors/RequestError'; import { has } from '@logto/essentials'; import { Middleware } from 'koa'; import koaBody from 'koa-body'; @@ -69,7 +69,7 @@ export default function koaGuard< params: params?.parse(ctx.params), } as Guarded; // Have to do t His since it's too complicated for TS } catch (error: unknown) { - throw new RequestError(GuardErrorCode.InvalidInput, error); + throw new RequestError('guard.invalid_input', error); } await next(); diff --git a/packages/core/src/routes/register.ts b/packages/core/src/routes/register.ts index a9d71afd0..4d9a0a2a2 100644 --- a/packages/core/src/routes/register.ts +++ b/packages/core/src/routes/register.ts @@ -5,7 +5,7 @@ import { hasUser, hasUserWithId, insertUser } from '@/queries/user'; import { customAlphabet, nanoid } from 'nanoid'; import { PasswordEncryptionMethod } from '@logto/schemas'; import koaGuard from '@/middleware/koa-guard'; -import RequestError, { RegisterErrorCode } from '@/errors/RequestError'; +import RequestError from '@/errors/RequestError'; const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; const userId = customAlphabet(alphabet, 12); @@ -37,7 +37,7 @@ export default function registerRoutes() { const { username, password } = ctx.guard.body; if (await hasUser(username)) { - throw new RequestError(RegisterErrorCode.UsernameExists); + throw new RequestError('register.username_exists'); } const id = await generateUserId(); diff --git a/packages/core/src/routes/sign-in.ts b/packages/core/src/routes/sign-in.ts index c7196a72e..e572da395 100644 --- a/packages/core/src/routes/sign-in.ts +++ b/packages/core/src/routes/sign-in.ts @@ -6,7 +6,8 @@ import { findUserByUsername } from '@/queries/user'; import { Provider } from 'oidc-provider'; import { conditional } from '@logto/essentials'; import koaGuard from '@/middleware/koa-guard'; -import RequestError, { OidcErrorCode, SignInErrorCode } from '@/errors/RequestError'; +import RequestError from '@/errors/RequestError'; +import { LogtoErrorCode } from '@logto/phrases'; export default function signInRoutes(provider: Provider) { const router = new Router(); @@ -22,7 +23,7 @@ export default function signInRoutes(provider: Provider) { if (name === 'login') { const { username, password } = ctx.guard.body; - assert(username && password, new RequestError(SignInErrorCode.InsufficientInfo)); + assert(username && password, new RequestError('sign_in.insufficient_info')); try { const { id, passwordEncrypted, passwordEncryptionMethod, passwordEncryptionSalt } = @@ -30,12 +31,12 @@ export default function signInRoutes(provider: Provider) { assert( passwordEncrypted && passwordEncryptionMethod && passwordEncryptionSalt, - new RequestError(SignInErrorCode.InvalidSignInMethod) + new RequestError('sign_in.invalid_sign_in_method') ); assert( encryptPassword(id, password, passwordEncryptionSalt, passwordEncryptionMethod) === passwordEncrypted, - new RequestError(SignInErrorCode.InvalidCredentials) + new RequestError('sign_in.invalid_credentials') ); const redirectTo = await provider.interactionResult( @@ -49,7 +50,7 @@ export default function signInRoutes(provider: Provider) { ctx.body = { redirectTo }; } catch (error: unknown) { if (!(error instanceof RequestError)) { - throw new RequestError(SignInErrorCode.InvalidCredentials); + throw new RequestError('sign_in.invalid_credentials'); } throw error; @@ -98,8 +99,9 @@ export default function signInRoutes(provider: Provider) { router.post('/sign-in/abort', async (ctx) => { await provider.interactionDetails(ctx.req, ctx.res); + const error: LogtoErrorCode = 'oidc.aborted'; const redirectTo = await provider.interactionResult(ctx.req, ctx.res, { - error: OidcErrorCode.Aborted, + error, }); ctx.body = { redirectTo }; }); diff --git a/packages/core/src/utils/zod.ts b/packages/core/src/utils/zod.ts index 172fc3961..dbc761816 100644 --- a/packages/core/src/utils/zod.ts +++ b/packages/core/src/utils/zod.ts @@ -1,6 +1,6 @@ import { OpenAPIV3 } from 'openapi-types'; import { ZodArray, ZodBoolean, ZodNumber, ZodObject, ZodOptional, ZodString } from 'zod'; -import RequestError, { SwaggerErrorCode } from '@/errors/RequestError'; +import RequestError from '@/errors/RequestError'; import { conditional } from '@logto/essentials'; export const zodTypeToSwagger = (config: unknown): OpenAPIV3.SchemaObject => { @@ -46,5 +46,5 @@ export const zodTypeToSwagger = (config: unknown): OpenAPIV3.SchemaObject => { }; } - throw new RequestError(SwaggerErrorCode.InvalidZodType, config); + throw new RequestError('swagger.invalid_zod_type', config); }; diff --git a/packages/phrases/README.md b/packages/phrases/README.md new file mode 100644 index 000000000..162a5718a --- /dev/null +++ b/packages/phrases/README.md @@ -0,0 +1,11 @@ +# `@logto/phrases` + +> TODO: description + +## Usage + +``` +const phrases = require('@logto/phrases'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/phrases/package.json b/packages/phrases/package.json new file mode 100644 index 000000000..d68afaa29 --- /dev/null +++ b/packages/phrases/package.json @@ -0,0 +1,43 @@ +{ + "name": "@logto/phrases", + "version": "0.1.0", + "description": "Logto shared phrases (l10n).", + "author": "Gao Sun ", + "homepage": "https://github.com/logto-io/logto#readme", + "license": "UNLICENSED", + "main": "lib/index.js", + "private": true, + "files": [ + "lib" + ], + "publishConfig": { + "registry": "https://registry.yarnpkg.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/logto-io/logto.git" + }, + "scripts": { + "build": "rm -rf lib/ && tsc", + "lint": "eslint --format pretty \"src/**\"", + "prepack": "yarn build" + }, + "bugs": { + "url": "https://github.com/logto-io/logto/issues" + }, + "devDependencies": { + "@logto/eslint-config": "^0.1.0-rc.14", + "@logto/ts-config": "^0.1.0-rc.14", + "eslint": "^7.31.0", + "eslint-formatter-pretty": "^4.1.0", + "prettier": "^2.3.2", + "typescript": "^4.3.5" + }, + "eslintConfig": { + "extends": "@logto" + }, + "prettier": "@logto/eslint-config/.prettierrc", + "dependencies": { + "@logto/schemas": "^0.1.0" + } +} diff --git a/packages/phrases/src/index.ts b/packages/phrases/src/index.ts new file mode 100644 index 000000000..d9ecc0917 --- /dev/null +++ b/packages/phrases/src/index.ts @@ -0,0 +1,12 @@ +import en from './locales/en'; +import zhCN from './locales/zh-cn'; +import { Normalize, Resource } from './types'; + +export type LogtoErrorCode = Normalize; + +const resource: Resource = { + en, + 'zh-CN': zhCN, +}; + +export default resource; diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts new file mode 100644 index 000000000..89ccf79aa --- /dev/null +++ b/packages/phrases/src/locales/en.ts @@ -0,0 +1,39 @@ +const translation = { + sign_in: { + title: 'Sign In', + loading: 'Signing in...', + error: 'Username or password is invalid.', + username: 'Username', + password: 'Password', + }, + register: { + create_account: 'Create an Account', + }, +}; + +const errors = { + guard: { + invalid_input: 'The request input is invalid.', + }, + oidc: { + aborted: 'The end-user aborted interaction.', + }, + register: { + username_exists: 'The username already exists.', + }, + sign_in: { + invalid_credentials: 'Invalid credentials. Please check your input.', + invalid_sign_in_method: 'Current sign-in method is not available.', + insufficient_info: 'Insufficent sign-in info.', + }, + swagger: { + invalid_zod_type: 'Invalid Zod type, please check route guard config.', + }, +}; + +const en = Object.freeze({ + translation, + errors, +}); + +export default en; diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts new file mode 100644 index 000000000..ff69daeab --- /dev/null +++ b/packages/phrases/src/locales/zh-cn.ts @@ -0,0 +1,41 @@ +import en from './en'; + +const translation = { + sign_in: { + title: '登录', + loading: '登录中...', + error: '用户名或密码错误。', + username: '用户名', + password: '密码', + }, + register: { + create_account: '新用户注册', + }, +}; + +const errors = { + guard: { + invalid_input: '请求内容有误。', + }, + oidc: { + aborted: '用户终止了交互。', + }, + register: { + username_exists: '用户名已存在。', + }, + sign_in: { + invalid_credentials: '用户名或密码错误,请检查您的输入。', + invalid_sign_in_method: '当前登录方式不可用。', + insufficient_info: '登录信息缺失,请检查您的输入。', + }, + swagger: { + invalid_zod_type: '无效的 Zod 类型,请检查路由 guard 配置。', + }, +}; + +const zhCN: typeof en = Object.freeze({ + translation, + errors, +}); + +export default zhCN; diff --git a/packages/phrases/src/types.ts b/packages/phrases/src/types.ts new file mode 100644 index 000000000..f6718f4c5 --- /dev/null +++ b/packages/phrases/src/types.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/consistent-indexed-object-style */ + +/* Copied from i18next/index.d.ts */ +export interface Resource { + [language: string]: ResourceLanguage; +} + +export interface ResourceLanguage { + [namespace: string]: ResourceKey; +} + +export type ResourceKey = + | string + | { + [key: string]: any; + }; + +/* Copied from react-i18next/ts4.1/index.d.ts */ +// Normalize single namespace +type AppendKeys = `${K1 & string}.${K2 & string}`; +type AppendKeys2 = `${K1 & string}.${Exclude & string}`; +type Normalize2 = K extends keyof T + ? T[K] extends Record + ? T[K] extends readonly any[] + ? AppendKeys2 | AppendKeys2> + : AppendKeys | AppendKeys> + : never + : never; +export type Normalize = keyof T | Normalize2; diff --git a/packages/phrases/tsconfig.json b/packages/phrases/tsconfig.json new file mode 100644 index 000000000..83d1932bb --- /dev/null +++ b/packages/phrases/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@logto/ts-config/tsconfig.base", + "compilerOptions": { + "outDir": "lib", + "declaration": true + }, + "include": ["src"] +} diff --git a/packages/ui/package.json b/packages/ui/package.json index 9f681191c..1f58d1d0d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -15,6 +15,7 @@ "test": "razzle test --env=jsdom" }, "dependencies": { + "@logto/phrases": "^0.1.0", "classnames": "^2.3.1", "i18next": "^20.3.3", "i18next-browser-languagedetector": "^6.1.2", diff --git a/packages/ui/src/components/TextLink/index.tsx b/packages/ui/src/components/TextLink/index.tsx index e8dd33137..af4edba89 100644 --- a/packages/ui/src/components/TextLink/index.tsx +++ b/packages/ui/src/components/TextLink/index.tsx @@ -1,10 +1,10 @@ -import React, { ReactChildren } from 'react'; +import React, { ReactChild } from 'react'; import classNames from 'classnames'; import styles from './index.module.scss'; export type Props = { className?: string; - children: ReactChildren; + children: ReactChild; href: string; }; diff --git a/packages/ui/src/include.d/react-i18next.d.ts b/packages/ui/src/include.d/react-i18next.d.ts index 573429fa9..adbe43039 100644 --- a/packages/ui/src/include.d/react-i18next.d.ts +++ b/packages/ui/src/include.d/react-i18next.d.ts @@ -2,7 +2,7 @@ // eslint-disable-next-line import/no-unassigned-import import 'react-i18next'; -import en from '@/locales/en.json'; +import en from '@logto/phrases/lib/locales/en.js'; declare module 'react-i18next' { interface CustomTypeOptions { diff --git a/packages/ui/src/init/i18n.ts b/packages/ui/src/init/i18n.ts index b45e62cff..304a84dab 100644 --- a/packages/ui/src/init/i18n.ts +++ b/packages/ui/src/init/i18n.ts @@ -1,18 +1,14 @@ import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; -import en from '@/locales/en.json'; -import zhCN from '@/locales/zh-CN.json'; +import resources from '@logto/phrases'; const initI18n = () => { void i18n .use(initReactI18next) .use(LanguageDetector) .init({ - resources: { - en, - 'zh-CN': zhCN, - }, + resources, fallbackLng: 'en', interpolation: { escapeValue: false, diff --git a/packages/ui/src/locales/en.json b/packages/ui/src/locales/en.json deleted file mode 100644 index d6e28811b..000000000 --- a/packages/ui/src/locales/en.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "translation": { - "sign_in": "Sign In", - "sign_in.loading": "Signing in...", - "sign_in.error": "Username or password is invalid.", - "sign_in.username": "Username", - "sign_in.password": "Password", - "register.create_account": "Create an Account" - } -} diff --git a/packages/ui/src/locales/zh-CN.json b/packages/ui/src/locales/zh-CN.json deleted file mode 100644 index 31cae0138..000000000 --- a/packages/ui/src/locales/zh-CN.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "translation": { - "sign_in": "登录", - "sign_in.loading": "登录中...", - "sign_in.error": "用户名或密码错误。", - "sign_in.username": "用户名", - "sign_in.password": "密码", - "register.create_account": "新用户注册" - } -} diff --git a/packages/ui/src/pages/SignIn/index.tsx b/packages/ui/src/pages/SignIn/index.tsx index 139f15a6e..f3d8d0e15 100644 --- a/packages/ui/src/pages/SignIn/index.tsx +++ b/packages/ui/src/pages/SignIn/index.tsx @@ -33,7 +33,7 @@ const Home = () => { @@ -50,7 +50,7 @@ const Home = () => { )}