Files
ragflow/web/src/locales/config.ts
Yoorim Choi 49ef959991 i18n(ko): add Korean (한국어) translation (#15863)
### What problem does this PR solve?

- Add `web/src/locales/ko.ts` with full Korean translation (~3100 keys)
- Register `Ko = 'ko'` in `LanguageAbbreviation` enum (`common.ts`)
- Add `[LanguageAbbreviation.Ko]: '한국어'` to `LanguageAbbreviationMap`
- Add lazy-load entry in `web/src/locales/config.ts`
- Add `korean` key to all existing locale files (`ja`, `id`, `es`,
`pt-br`, `vi`, `zh-traditional`)
- Fix duplicate enum value `FileMimeType.Mdx` (`'text/markdown'` →
`'text/mdx'`)

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
- [x] Other (please describe): Korean (한국어) i18n translation + fix
duplicate FileMimeType.Mdx enum value
2026-06-11 16:55:40 +08:00

124 lines
3.8 KiB
TypeScript

import { LanguageAbbreviation } from '@/constants/common';
import storage from '@/utils/authorization-util';
import dayjs from 'dayjs';
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { upperFirst } from 'lodash';
import { initReactI18next } from 'react-i18next';
import translation_en from './en';
//The language is based on the .ng file stored in the client's local storage.
// The language stored in the database is for agent template resources, as these resources reside on the server.
// When a user logs in from a different machine, the login page language is the language configured by VITE_DEFAULT_LANGUAGE_CODE.
const languageImports: Record<string, () => Promise<{ default: any }>> = {
[LanguageAbbreviation.En]: () => import('./en'),
[LanguageAbbreviation.Zh]: () => import('./zh'),
[LanguageAbbreviation.ZhTraditional]: () => import('./zh-traditional'),
[LanguageAbbreviation.Id]: () => import('./id'),
[LanguageAbbreviation.Ja]: () => import('./ja'),
[LanguageAbbreviation.Es]: () => import('./es'),
[LanguageAbbreviation.Vi]: () => import('./vi'),
[LanguageAbbreviation.Ru]: () => import('./ru'),
[LanguageAbbreviation.PtBr]: () => import('./pt-br'),
[LanguageAbbreviation.De]: () => import('./de'),
[LanguageAbbreviation.Fr]: () => import('./fr'),
[LanguageAbbreviation.It]: () => import('./it'),
[LanguageAbbreviation.Bg]: () => import('./bg'),
[LanguageAbbreviation.Ar]: () => import('./ar'),
[LanguageAbbreviation.Tr]: () => import('./tr'),
[LanguageAbbreviation.Ko]: () => import('./ko'),
};
const supportedLanguageCodes: Intl.UnicodeBCP47LocaleIdentifier[] =
Object.keys(languageImports);
export const supportedLanguages = supportedLanguageCodes.map((code) => {
const locale = new Intl.Locale(code);
return {
code,
locale,
displayName: upperFirst(
new Intl.DisplayNames(locale, { type: 'language' }).of(code)!,
),
};
});
export const DEFAULT_LANGUAGE_CODE =
import.meta.env.VITE_DEFAULT_LANGUAGE_CODE || LanguageAbbreviation.En;
const resources = {
[LanguageAbbreviation.En]: translation_en,
};
const updateDocumentLocale = (lng: string) => {
document.documentElement.lang = lng;
document.documentElement.dir = 'ltr';
dayjs.locale(lng === 'zh' ? 'zh-cn' : lng);
};
i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
detection: {
lookupLocalStorage: 'lng',
order: ['localStorage'],
caches: [],
},
supportedLngs: supportedLanguageCodes,
resources,
fallbackLng: DEFAULT_LANGUAGE_CODE,
interpolation: {
escapeValue: false,
},
});
export const loadLanguageAsync = async (lng: string): Promise<void> => {
const normalizedLng = lng;
if (i18n.hasResourceBundle(normalizedLng, 'translation')) {
return;
}
const importFn = languageImports[normalizedLng];
if (!importFn) {
console.warn(`Language ${lng} is not supported for lazy loading`);
return;
}
try {
const module = await importFn();
const translationData = module.default?.translation || module.default;
i18n.addResourceBundle(normalizedLng, 'translation', translationData);
} catch (error) {
console.error(`Failed to load language ${lng}:`, error);
}
};
export const changeLanguageAsync = async (lng: string): Promise<void> => {
const normalizedLng = lng;
if (
normalizedLng !== LanguageAbbreviation.En &&
!i18n.hasResourceBundle(normalizedLng, 'translation')
) {
await loadLanguageAsync(normalizedLng);
}
storage.setLanguage(lng);
updateDocumentLocale(lng);
await i18n.changeLanguage(normalizedLng);
};
export const initLanguage = async (): Promise<void> => {
const currentLng = storage.getLanguage() || DEFAULT_LANGUAGE_CODE;
await changeLanguageAsync(currentLng);
};
export default i18n;