0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

refactor(phrases,shared,connector-core): move language key to shared (#1838)

This commit is contained in:
Wang Sijie 2022-08-30 16:53:49 +08:00 committed by GitHub
parent 3d92f35589
commit d952d8660d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 165 additions and 316 deletions

View file

@ -18,7 +18,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/phrases": "^1.0.0-beta.6",
"@logto/shared": "^1.0.0-beta.6",
"@silverhand/essentials": "^1.2.0",
"zod": "^3.14.3"
},

View file

@ -1,4 +1,4 @@
import type { Language } from '@logto/phrases';
import type { LanguageKey } from '@logto/shared';
import { Nullable } from '@silverhand/essentials';
import { z, ZodType } from 'zod';
@ -14,8 +14,8 @@ export enum ConnectorPlatform {
Web = 'Web',
}
type I18nPhrases = { [Language.English]: string } & {
[key in Exclude<Language, Language.English>]?: string;
type I18nPhrases = { en: string } & {
[K in Exclude<LanguageKey, 'en'>]?: string;
};
export type ConnectorMetadata = {

View file

@ -1,6 +1,6 @@
import { Language } from '@logto/phrases';
import { useLogto } from '@logto/react';
import { AppearanceMode } from '@logto/schemas';
import { languageKeys } from '@logto/shared';
import { Nullable, Optional } from '@silverhand/essentials';
import { useCallback, useEffect, useMemo } from 'react';
import useSWR from 'swr';
@ -11,7 +11,7 @@ import { themeStorageKey } from '@/consts';
import useApi, { RequestError } from './use-api';
const userPreferencesGuard = z.object({
language: z.nativeEnum(Language).optional(),
language: z.enum(languageKeys).optional(),
appearanceMode: z.nativeEnum(AppearanceMode),
experienceNoticeConfirmed: z.boolean().optional(),
getStartedHidden: z.boolean().optional(),

View file

@ -1,9 +1,10 @@
import resources, { Language } from '@logto/phrases';
import resources from '@logto/phrases';
import type { LanguageKey } from '@logto/shared';
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
const initI18n = async (language?: Language) =>
const initI18n = async (language?: LanguageKey) =>
i18next
.use(initReactI18next)
.use(LanguageDetector)

View file

@ -1,5 +1,5 @@
import { languageEnumGuard } from '@logto/phrases';
import { ConnectorDto, ConnectorType } from '@logto/schemas';
import { languageKeyGuard } from '@logto/shared';
import { conditional } from '@silverhand/essentials';
import i18next from 'i18next';
import { Controller, useForm } from 'react-hook-form';
@ -33,7 +33,7 @@ const Guide = ({ connector, onClose }: Props) => {
const { id: connectorId, type: connectorType, name, configTemplate, readme } = connector;
const localeRaw = i18next.language;
const result = languageEnumGuard.safeParse(localeRaw);
const result = languageKeyGuard.safeParse(localeRaw);
const connectorName = result.success ? name[result.data] : name.en;
const isSocialConnector =

View file

@ -1,5 +1,6 @@
import { Language, languageOptions } from '@logto/phrases';
import { languageOptions } from '@logto/phrases';
import { AppearanceMode } from '@logto/schemas';
import { languageKeyGuard, languageKeys } from '@logto/shared';
import classNames from 'classnames';
import { Controller, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -24,9 +25,7 @@ const Settings = () => {
i18n: { language },
} = useTranslation(undefined, { keyPrefix: 'admin_console' });
const defaultLanguage = Object.values<string>(Language).includes(language)
? language
: Language.English;
const defaultLanguage = languageKeyGuard.default('en').parse(language);
const { data, error, update, isLoading, isLoaded } = useUserPreferences();
const {

View file

@ -1,7 +1,8 @@
// FIXME: @sijie
/* eslint-disable react/iframe-missing-sandbox */
import { Language, languageOptions } from '@logto/phrases-ui';
import { languageOptions } from '@logto/phrases-ui';
import { AppearanceMode, ConnectorDto, ConnectorMetadata, SignInExperience } from '@logto/schemas';
import type { LanguageKey } from '@logto/shared';
import { conditional } from '@silverhand/essentials';
import classNames from 'classnames';
import dayjs from 'dayjs';
@ -24,7 +25,7 @@ type Props = {
const Preview = ({ signInExperience, className }: Props) => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [language, setLanguage] = useState<Language>(Language.English);
const [language, setLanguage] = useState<LanguageKey>('en');
const [mode, setMode] = useState<AppearanceMode>(AppearanceMode.LightMode);
const [platform, setPlatform] = useState<'desktopWeb' | 'mobile' | 'mobileWeb'>('desktopWeb');
const { data: allConnectors } = useSWR<ConnectorDto[], RequestError>('/api/connectors');

View file

@ -1,5 +1,5 @@
import { Language } from '@logto/phrases';
import { SignInExperience, SignInMethodKey } from '@logto/schemas';
import type { LanguageKey } from '@logto/shared';
export enum LanguageMode {
Auto = 'Auto',
@ -17,8 +17,8 @@ export type SignInExperienceForm = Omit<SignInExperience, 'signInMethods' | 'lan
};
languageInfo: {
mode: LanguageMode;
fixedLanguage: Language;
fallbackLanguage: Language;
fixedLanguage: LanguageKey;
fallbackLanguage: LanguageKey;
};
createAccountEnabled: boolean;
};

View file

@ -1,4 +1,3 @@
import { Language } from '@logto/phrases';
import {
Branding,
BrandingStyle,
@ -28,8 +27,8 @@ export const mockSignInExperience: SignInExperience = {
},
languageInfo: {
autoDetect: true,
fallbackLanguage: Language.English,
fixedLanguage: Language.Chinese,
fallbackLanguage: 'en',
fixedLanguage: 'zh-CN',
},
signInMethods: {
username: SignInMethodState.Primary,
@ -60,8 +59,8 @@ export const mockTermsOfUse: TermsOfUse = {
export const mockLanguageInfo: LanguageInfo = {
autoDetect: true,
fallbackLanguage: Language.English,
fixedLanguage: Language.Chinese,
fallbackLanguage: 'en',
fixedLanguage: 'zh-CN',
};
export const mockSignInMethods: SignInMethods = {

View file

@ -1,5 +1,5 @@
import { Language } from '@logto/phrases';
import { CreateSignInExperience, SignInExperience, SignInMethodState } from '@logto/schemas';
import { languageKeys } from '@logto/shared';
import {
mockAliyunDmConnector,
@ -104,7 +104,7 @@ describe('languageInfo', () => {
});
});
const validLanguages = Object.values(Language);
const validLanguages = languageKeys;
const invalidLanguages = [undefined, null, '', ' \t\n\r', 'abc'];
describe('fallbackLanguage', () => {

View file

@ -4,6 +4,7 @@ import { OpenAPIV3 } from 'openapi-types';
import {
ZodArray,
ZodBoolean,
ZodEnum,
ZodLiteral,
ZodNativeEnum,
ZodNullable,
@ -111,7 +112,7 @@ export const zodTypeToSwagger = (config: unknown): OpenAPIV3.SchemaObject => {
};
}
if (config instanceof ZodNativeEnum) {
if (config instanceof ZodNativeEnum || config instanceof ZodEnum) {
return {
type: 'string',
enum: Object.values(config.enum),

View file

@ -1,9 +1,10 @@
import resources, { Language } from '@logto/phrases';
import resources from '@logto/phrases';
import type { LanguageKey } from '@logto/shared';
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
const initI18n = async (language?: Language) =>
const initI18n = async (language?: LanguageKey) =>
i18next
.use(initReactI18next)
.use(LanguageDetector)

View file

@ -29,6 +29,7 @@
"url": "https://github.com/logto-io/logto/issues"
},
"dependencies": {
"@logto/shared": "^1.0.0-beta.6",
"@silverhand/essentials": "^1.2.0"
},
"devDependencies": {

View file

@ -5,18 +5,18 @@ import fr from './locales/fr';
import koKR from './locales/ko-kr';
import trTR from './locales/tr-tr';
import zhCN from './locales/zh-cn';
import { Resource, Language } from './types';
import { Resource } from './types';
export { Language, languageOptions } from './types';
export { languageOptions } from './types';
export type I18nKey = NormalizeKeyPaths<typeof en.translation>;
const resource: Resource = {
[Language.English]: en,
[Language.French]: fr,
[Language.Chinese]: zhCN,
[Language.Korean]: koKR,
[Language.Turkish]: trTR,
en,
fr,
'zh-CN': zhCN,
'ko-KR': koKR,
'tr-TR': trTR,
};
export default resource;

View file

@ -1,28 +1,20 @@
import { LanguageKey, languageKeyGuard } from '@logto/shared';
/* Copied from i18next/index.d.ts */
export type Resource = Record<Language, ResourceLanguage>;
export type Resource = Record<LanguageKey, ResourceLanguage>;
export type ResourceLanguage = Record<string, ResourceKey>;
export type ResourceKey = string | Record<string, unknown>;
export enum Language {
English = 'en',
French = 'fr',
Chinese = 'zh-CN',
Turkish = 'tr-TR',
Korean = 'ko-KR',
}
const languageCodeAndDisplayNameMappings: Record<Language, string> = {
[Language.English]: 'English',
[Language.French]: 'Français',
[Language.Chinese]: '简体中文',
[Language.Turkish]: 'Türkçe',
[Language.Korean]: '한국어',
const languageCodeAndDisplayNameMappings: Record<LanguageKey, string> = {
en: 'English',
fr: 'Français',
'zh-CN': '简体中文',
'tr-TR': 'Türkçe',
'ko-KR': '한국어',
};
export const languageOptions = Object.entries(languageCodeAndDisplayNameMappings)
.filter((entry): entry is [Language, string] =>
Object.values<string>(Language).includes(entry[0])
)
.map(([key, value]) => ({ value: key, title: value }));
export const languageOptions = Object.entries(languageCodeAndDisplayNameMappings).map(
([key, value]) => ({ value: languageKeyGuard.parse(key), title: value })
);

View file

@ -29,8 +29,8 @@
"url": "https://github.com/logto-io/logto/issues"
},
"dependencies": {
"@silverhand/essentials": "^1.2.0",
"zod": "^3.14.3"
"@logto/shared": "^1.0.0-beta.6",
"@silverhand/essentials": "^1.2.0"
},
"devDependencies": {
"@silverhand/eslint-config": "1.0.0-rc.2",

View file

@ -5,9 +5,9 @@ import fr from './locales/fr';
import koKR from './locales/ko-kr';
import trTR from './locales/tr-tr';
import zhCN from './locales/zh-cn';
import { Resource, Language } from './types';
import { Resource } from './types';
export { Language, languageOptions, languageEnumGuard } from './types';
export { languageOptions } from './types';
export type Translation = typeof en.translation;
export type Errors = typeof en.errors;
export type LogtoErrorCode = NormalizeKeyPaths<Errors>;
@ -16,11 +16,11 @@ export type I18nKey = NormalizeKeyPaths<Translation>;
export type AdminConsoleKey = NormalizeKeyPaths<typeof en.translation.admin_console>;
const resource: Resource = {
[Language.English]: en,
[Language.French]: fr,
[Language.Chinese]: zhCN,
[Language.Korean]: koKR,
[Language.Turkish]: trTR,
en,
fr,
'zh-CN': zhCN,
'ko-KR': koKR,
'tr-TR': trTR,
};
export default resource;

View file

@ -1,32 +1,20 @@
import { z } from 'zod';
import { LanguageKey, languageKeyGuard } from '@logto/shared';
/* Copied from i18next/index.d.ts */
export type Resource = Record<Language, ResourceLanguage>;
export type Resource = Record<LanguageKey, ResourceLanguage>;
export type ResourceLanguage = Record<string, ResourceKey>;
export type ResourceKey = string | Record<string, unknown>;
export enum Language {
English = 'en',
French = 'fr',
Chinese = 'zh-CN',
Turkish = 'tr-TR',
Korean = 'ko-KR',
}
export const languageEnumGuard = z.nativeEnum(Language);
const languageCodeAndDisplayNameMappings: Record<Language, string> = {
[Language.English]: 'English',
[Language.French]: 'Français',
[Language.Chinese]: '简体中文',
[Language.Turkish]: 'Türkçe',
[Language.Korean]: '한국어',
const languageCodeAndDisplayNameMappings: Record<LanguageKey, string> = {
en: 'English',
fr: 'Français',
'zh-CN': '简体中文',
'tr-TR': 'Türkçe',
'ko-KR': '한국어',
};
export const languageOptions = Object.entries(languageCodeAndDisplayNameMappings)
.filter((entry): entry is [Language, string] =>
Object.values<string>(Language).includes(entry[0])
)
.map(([key, value]) => ({ value: key, title: value }));
export const languageOptions: Array<{ value: LanguageKey; title: string }> = Object.entries(
languageCodeAndDisplayNameMappings
).map(([key, value]) => ({ value: languageKeyGuard.parse(key), title: value }));

View file

@ -1,5 +1,4 @@
import { Language } from '@logto/phrases-ui';
import { hexColorRegEx } from '@logto/shared';
import { hexColorRegEx, languageKeys } from '@logto/shared';
import { z } from 'zod';
/**
@ -103,8 +102,8 @@ export type TermsOfUse = z.infer<typeof termsOfUseGuard>;
export const languageInfoGuard = z.object({
autoDetect: z.boolean(),
fallbackLanguage: z.nativeEnum(Language),
fixedLanguage: z.nativeEnum(Language),
fallbackLanguage: z.enum(languageKeys),
fixedLanguage: z.enum(languageKeys),
});
export type LanguageInfo = z.infer<typeof languageInfoGuard>;

View file

@ -1,5 +1,3 @@
import { Language } from '@logto/phrases';
import { CreateSetting } from '../db-entries';
import { AppearanceMode } from '../foundations';
@ -9,7 +7,7 @@ export const createDefaultSetting = (): Readonly<CreateSetting> =>
Object.freeze({
id: defaultSettingId,
adminConsole: {
language: Language.English,
language: 'en',
appearanceMode: AppearanceMode.SyncWithSystem,
demoChecked: false,
applicationCreated: false,

View file

@ -1,4 +1,3 @@
import { Language } from '@logto/phrases-ui';
import { generateDarkColor } from '@logto/shared';
import { CreateSignInExperience, SignInMode } from '../db-entries';
@ -20,8 +19,8 @@ export const defaultSignInExperience: Readonly<CreateSignInExperience> = {
},
languageInfo: {
autoDetect: true,
fallbackLanguage: Language.English,
fixedLanguage: Language.English,
fallbackLanguage: 'en',
fixedLanguage: 'en',
},
termsOfUse: {
enabled: false,

View file

@ -23,7 +23,8 @@
},
"dependencies": {
"color": "^4.2.3",
"nanoid": "^3.1.23"
"nanoid": "^3.1.23",
"zod": "^3.18.0"
},
"devDependencies": {
"@silverhand/eslint-config": "1.0.0-rc.2",

View file

@ -1,2 +1,3 @@
export * from './utilities';
export * from './regex';
export * from './language';

View file

@ -0,0 +1,5 @@
import { z } from 'zod';
export const languageKeys = ['en', 'fr', 'zh-CN', 'tr-TR', 'ko-KR'] as const;
export const languageKeyGuard = z.enum(languageKeys);
export type LanguageKey = z.infer<typeof languageKeyGuard>;

View file

@ -1,4 +1,3 @@
import { Language } from '@logto/phrases-ui';
import {
BrandingStyle,
ConnectorPlatform,
@ -166,8 +165,8 @@ export const mockSignInExperience: SignInExperience = {
},
languageInfo: {
autoDetect: true,
fallbackLanguage: Language.English,
fixedLanguage: Language.Chinese,
fallbackLanguage: 'en',
fixedLanguage: 'zh-CN',
},
signInMethods: {
username: SignInMethodState.Primary,

View file

@ -1,4 +1,3 @@
import { Language } from '@logto/phrases-ui';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
@ -20,7 +19,7 @@ const SocialLinkButton = ({ isDisabled, className, target, name, logo, onClick }
i18n: { language },
} = useTranslation();
const localName = name[language] ?? name[Language.English];
const localName = name[language] ?? name.en;
return (
<button

View file

@ -1,4 +1,4 @@
import { Language } from '@logto/phrases-ui';
import { languageKeyGuard } from '@logto/shared';
import { useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -48,7 +48,7 @@ const SocialSignInDropdown = ({ isOpen, onClose, connectors, anchorRef }: Props)
>
{connectors.map((connector) => {
const { id, name, logo, logoDark } = connector;
const localName = isKeyOf(language, name) ? name[language] : name[Language.English];
const localName = name[languageKeyGuard.default('en').parse(language)];
return (
<DropdownItem

View file

@ -1,5 +1,5 @@
import { Language } from '@logto/phrases-ui';
import { SignInExperience, ConnectorMetadata, AppearanceMode } from '@logto/schemas';
import type { LanguageKey } from '@logto/shared';
export type UserFlow = 'sign-in' | 'register';
export type SignInMethod = 'username' | 'email' | 'sms' | 'social';
@ -36,7 +36,7 @@ export enum TermsOfUseModalMessage {
export type PreviewConfig = {
signInExperience: SignInExperienceSettingsResponse;
language: Language;
language: LanguageKey;
mode: AppearanceMode.LightMode | AppearanceMode.DarkMode;
platform: Platform;
isNative: boolean;

281
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff