mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(i18n): default interface language to english
This commit is contained in:
@@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|||||||
import packageJson from '../../../../../package.json';
|
import packageJson from '../../../../../package.json';
|
||||||
import InterfaceSettings from '../interface-settings';
|
import InterfaceSettings from '../interface-settings';
|
||||||
import useFeedViewSettingsStore from '../../../../stores/use-feed-view-settings-store';
|
import useFeedViewSettingsStore from '../../../../stores/use-feed-view-settings-store';
|
||||||
|
import { INTERFACE_LANGUAGE_STORAGE_KEY } from '../../../../lib/constants';
|
||||||
|
|
||||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||||
@@ -155,6 +156,7 @@ describe('InterfaceSettings', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(testState.changeLanguageMock).toHaveBeenCalledWith('fr');
|
expect(testState.changeLanguageMock).toHaveBeenCalledWith('fr');
|
||||||
|
expect(localStorage.getItem(INTERFACE_LANGUAGE_STORAGE_KEY)).toBe('fr');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('disables the update button while fetching and restores it afterward', async () => {
|
it('disables the update button while fetching and restores it afterward', async () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import capitalize from 'lodash/capitalize';
|
|||||||
import useExpandedMediaStore from '../../../stores/use-expanded-media-store';
|
import useExpandedMediaStore from '../../../stores/use-expanded-media-store';
|
||||||
import useFeedViewSettingsStore from '../../../stores/use-feed-view-settings-store';
|
import useFeedViewSettingsStore from '../../../stores/use-feed-view-settings-store';
|
||||||
import Version from '../../version';
|
import Version from '../../version';
|
||||||
|
import { INTERFACE_LANGUAGE_STORAGE_KEY, SUPPORTED_INTERFACE_LANGUAGES } from '../../../lib/constants';
|
||||||
|
|
||||||
const commitRef = process.env.VITE_COMMIT_REF;
|
const commitRef = process.env.VITE_COMMIT_REF;
|
||||||
const isElectron = window.electronApi?.isElectron === true;
|
const isElectron = window.electronApi?.isElectron === true;
|
||||||
@@ -67,21 +68,20 @@ const CheckForUpdates = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const availableLanguages = ['ar', 'bn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fil', 'fr', 'he', 'hi', 'hu', 'id', 'it', 'ja', 'ko', 'mr', 'nl', 'no', 'pl', 'pt', 'ro', 'ru', 'sq', 'sv', 'te', 'th', 'tr', 'uk', 'ur', 'vi', 'zh'];
|
|
||||||
|
|
||||||
const InterfaceLanguage = () => {
|
const InterfaceLanguage = () => {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const { changeLanguage, language } = i18n;
|
const { changeLanguage, language } = i18n;
|
||||||
|
|
||||||
const onSelectLanguage = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const onSelectLanguage = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
changeLanguage(e.target.value);
|
const selectedLanguage = e.target.value;
|
||||||
|
localStorage.setItem(INTERFACE_LANGUAGE_STORAGE_KEY, selectedLanguage);
|
||||||
|
changeLanguage(selectedLanguage);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.languageSettings}>
|
<div className={styles.languageSettings}>
|
||||||
<select value={language} onChange={onSelectLanguage}>
|
<select value={language} onChange={onSelectLanguage}>
|
||||||
{availableLanguages.map((lang) => (
|
{SUPPORTED_INTERFACE_LANGUAGES.map((lang) => (
|
||||||
<option key={lang} value={lang}>
|
<option key={lang} value={lang}>
|
||||||
{lang}
|
{lang}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import { DEFAULT_INTERFACE_LANGUAGE, INTERFACE_LANGUAGE_STORAGE_KEY } from '../constants';
|
||||||
|
|
||||||
const testState = vi.hoisted(() => {
|
const testState = vi.hoisted(() => {
|
||||||
const i18next = {
|
const i18next = {
|
||||||
@@ -47,10 +48,15 @@ describe('init-translations', () => {
|
|||||||
expect(testState.i18next.use).toHaveBeenNthCalledWith(3, testState.reactPlugin);
|
expect(testState.i18next.use).toHaveBeenNthCalledWith(3, testState.reactPlugin);
|
||||||
expect(testState.i18next.init).toHaveBeenCalledWith(
|
expect(testState.i18next.init).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
fallbackLng: 'en',
|
fallbackLng: DEFAULT_INTERFACE_LANGUAGE,
|
||||||
ns: ['default'],
|
ns: ['default'],
|
||||||
defaultNS: 'default',
|
defaultNS: 'default',
|
||||||
backend: { loadPath: './translations/{{lng}}/{{ns}}.json' },
|
backend: { loadPath: './translations/{{lng}}/{{ns}}.json' },
|
||||||
|
detection: {
|
||||||
|
order: ['localStorage'],
|
||||||
|
caches: ['localStorage'],
|
||||||
|
lookupLocalStorage: INTERFACE_LANGUAGE_STORAGE_KEY,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
expect(testState.i18next.init.mock.calls[0]?.[0]?.supportedLngs).toContain('en');
|
expect(testState.i18next.init.mock.calls[0]?.[0]?.supportedLngs).toContain('en');
|
||||||
|
|||||||
@@ -6,3 +6,47 @@ export const BOARD_REPLIES_PREVIEW_VISIBLE_COUNT = 5;
|
|||||||
|
|
||||||
/** Number of replies fetched for preview mode on board/listing views. */
|
/** Number of replies fetched for preview mode on board/listing views. */
|
||||||
export const BOARD_REPLIES_PREVIEW_FETCH_SIZE = 25;
|
export const BOARD_REPLIES_PREVIEW_FETCH_SIZE = 25;
|
||||||
|
|
||||||
|
/** 5chan ships in English by default; users must opt into other languages manually. */
|
||||||
|
export const DEFAULT_INTERFACE_LANGUAGE = 'en';
|
||||||
|
|
||||||
|
/** Persist only deliberate in-app language selections, never OS/browser auto-detection. */
|
||||||
|
export const INTERFACE_LANGUAGE_STORAGE_KEY = '5chan-interface-language';
|
||||||
|
|
||||||
|
export const SUPPORTED_INTERFACE_LANGUAGES = [
|
||||||
|
'ar',
|
||||||
|
'bn',
|
||||||
|
'cs',
|
||||||
|
'da',
|
||||||
|
'de',
|
||||||
|
'el',
|
||||||
|
'en',
|
||||||
|
'es',
|
||||||
|
'fa',
|
||||||
|
'fi',
|
||||||
|
'fil',
|
||||||
|
'fr',
|
||||||
|
'he',
|
||||||
|
'hi',
|
||||||
|
'hu',
|
||||||
|
'id',
|
||||||
|
'it',
|
||||||
|
'ja',
|
||||||
|
'ko',
|
||||||
|
'mr',
|
||||||
|
'nl',
|
||||||
|
'no',
|
||||||
|
'pl',
|
||||||
|
'pt',
|
||||||
|
'ro',
|
||||||
|
'ru',
|
||||||
|
'sq',
|
||||||
|
'sv',
|
||||||
|
'te',
|
||||||
|
'th',
|
||||||
|
'tr',
|
||||||
|
'uk',
|
||||||
|
'ur',
|
||||||
|
'vi',
|
||||||
|
'zh',
|
||||||
|
] as const;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import i18next from 'i18next';
|
|||||||
import HttpBackend from 'i18next-http-backend';
|
import HttpBackend from 'i18next-http-backend';
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
import { DEFAULT_INTERFACE_LANGUAGE, INTERFACE_LANGUAGE_STORAGE_KEY, SUPPORTED_INTERFACE_LANGUAGES } from './constants';
|
||||||
|
|
||||||
const loadPath = `./translations/{{lng}}/{{ns}}.json`;
|
const loadPath = `./translations/{{lng}}/{{ns}}.json`;
|
||||||
|
|
||||||
@@ -10,44 +11,13 @@ i18next
|
|||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
fallbackLng: 'en',
|
fallbackLng: DEFAULT_INTERFACE_LANGUAGE,
|
||||||
supportedLngs: [
|
supportedLngs: [...SUPPORTED_INTERFACE_LANGUAGES],
|
||||||
'ar',
|
detection: {
|
||||||
'bn',
|
order: ['localStorage'],
|
||||||
'cs',
|
caches: ['localStorage'],
|
||||||
'da',
|
lookupLocalStorage: INTERFACE_LANGUAGE_STORAGE_KEY,
|
||||||
'de',
|
},
|
||||||
'el',
|
|
||||||
'en',
|
|
||||||
'es',
|
|
||||||
'fa',
|
|
||||||
'fi',
|
|
||||||
'fil',
|
|
||||||
'fr',
|
|
||||||
'he',
|
|
||||||
'hi',
|
|
||||||
'hu',
|
|
||||||
'id',
|
|
||||||
'it',
|
|
||||||
'ja',
|
|
||||||
'ko',
|
|
||||||
'mr',
|
|
||||||
'nl',
|
|
||||||
'no',
|
|
||||||
'pl',
|
|
||||||
'pt',
|
|
||||||
'ro',
|
|
||||||
'ru',
|
|
||||||
'sq',
|
|
||||||
'sv',
|
|
||||||
'te',
|
|
||||||
'th',
|
|
||||||
'tr',
|
|
||||||
'uk',
|
|
||||||
'ur',
|
|
||||||
'vi',
|
|
||||||
'zh',
|
|
||||||
],
|
|
||||||
|
|
||||||
ns: ['default'],
|
ns: ['default'],
|
||||||
defaultNS: 'default',
|
defaultNS: 'default',
|
||||||
|
|||||||
Reference in New Issue
Block a user