15
packages/frontend/core/src/modules/i18n/context.tsx
Normal file
15
packages/frontend/core/src/modules/i18n/context.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { I18nextProvider } from '@affine/i18n';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { type PropsWithChildren, useEffect } from 'react';
|
||||
|
||||
import { I18nService } from './services/i18n';
|
||||
|
||||
export function I18nProvider({ children }: PropsWithChildren) {
|
||||
const i18n = useService(I18nService).i18n;
|
||||
|
||||
useEffect(() => {
|
||||
i18n.init();
|
||||
}, [i18n]);
|
||||
|
||||
return <I18nextProvider i18n={i18n.i18next}>{children}</I18nextProvider>;
|
||||
}
|
||||
83
packages/frontend/core/src/modules/i18n/entities/i18n.ts
Normal file
83
packages/frontend/core/src/modules/i18n/entities/i18n.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { notify } from '@affine/component';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import {
|
||||
getOrCreateI18n,
|
||||
i18nCompletenesses,
|
||||
type Language,
|
||||
SUPPORTED_LANGUAGES,
|
||||
} from '@affine/i18n';
|
||||
import type { GlobalCache } from '@toeverything/infra';
|
||||
import { effect, Entity, fromPromise, LiveData } from '@toeverything/infra';
|
||||
import { catchError, EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||
|
||||
export type LanguageInfo = {
|
||||
key: Language;
|
||||
name: string;
|
||||
originalName: string;
|
||||
completeness: number;
|
||||
};
|
||||
|
||||
const logger = new DebugLogger('i18n');
|
||||
|
||||
function mapLanguageInfo(language: Language = 'en'): LanguageInfo {
|
||||
const languageInfo = SUPPORTED_LANGUAGES[language];
|
||||
|
||||
return {
|
||||
key: language,
|
||||
name: languageInfo.name,
|
||||
originalName: languageInfo.originalName,
|
||||
completeness: i18nCompletenesses[language],
|
||||
};
|
||||
}
|
||||
|
||||
export class I18n extends Entity {
|
||||
private readonly i18n = getOrCreateI18n();
|
||||
|
||||
get i18next() {
|
||||
return this.i18n;
|
||||
}
|
||||
|
||||
readonly currentLanguageKey$ = LiveData.from(
|
||||
this.cache.watch<Language>('i18n_lng'),
|
||||
undefined
|
||||
);
|
||||
|
||||
readonly currentLanguage$ = this.currentLanguageKey$
|
||||
.distinctUntilChanged()
|
||||
.map(mapLanguageInfo);
|
||||
|
||||
readonly languageList: Array<LanguageInfo> =
|
||||
// @ts-expect-error same key indexing
|
||||
Object.keys(SUPPORTED_LANGUAGES).map(mapLanguageInfo);
|
||||
|
||||
constructor(private readonly cache: GlobalCache) {
|
||||
super();
|
||||
this.i18n.on('languageChanged', (language: Language) => {
|
||||
document.documentElement.lang = language;
|
||||
this.cache.set('i18n_lng', language);
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
this.changeLanguage(this.currentLanguageKey$.value ?? 'en');
|
||||
}
|
||||
|
||||
changeLanguage = effect(
|
||||
exhaustMap((language: string) =>
|
||||
fromPromise(() => this.i18n.changeLanguage(language)).pipe(
|
||||
catchError(error => {
|
||||
notify({
|
||||
theme: 'error',
|
||||
title: 'Failed to change language',
|
||||
message: 'Error occurs when loading language files',
|
||||
});
|
||||
|
||||
logger.error('Failed to change language', error);
|
||||
|
||||
return EMPTY;
|
||||
}),
|
||||
mergeMap(() => EMPTY)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
11
packages/frontend/core/src/modules/i18n/index.ts
Normal file
11
packages/frontend/core/src/modules/i18n/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { type Framework, GlobalCache } from '@toeverything/infra';
|
||||
|
||||
import { I18nProvider } from './context';
|
||||
import { I18n, type LanguageInfo } from './entities/i18n';
|
||||
import { I18nService } from './services/i18n';
|
||||
|
||||
export function configureI18nModule(framework: Framework) {
|
||||
framework.service(I18nService).entity(I18n, [GlobalCache]);
|
||||
}
|
||||
|
||||
export { I18n, I18nProvider, I18nService, type LanguageInfo };
|
||||
7
packages/frontend/core/src/modules/i18n/services/i18n.ts
Normal file
7
packages/frontend/core/src/modules/i18n/services/i18n.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Service } from '@toeverything/infra';
|
||||
|
||||
import { I18n } from '../entities/i18n';
|
||||
|
||||
export class I18nService extends Service {
|
||||
public readonly i18n = this.framework.createEntity(I18n);
|
||||
}
|
||||
Reference in New Issue
Block a user