feat: Replace dictonary i18n with react-i18next (#2915)

This commit is contained in:
Timon
2024-08-26 07:39:50 +02:00
committed by GitHub
parent 9343f1e070
commit cf02ea2572
12 changed files with 173 additions and 101 deletions

View File

@@ -37,14 +37,15 @@
"graphql": "^16.6.0",
"graphql-request": "^3.7.0",
"httpsnippet": "^3.0.6",
"i18next": "^23.14.0",
"idb": "^7.0.0",
"immer": "^9.0.15",
"jsesc": "^3.0.2",
"jshint": "^2.13.6",
"json5": "^2.2.3",
"jsonc-parser": "^3.2.1",
"jsonpath-plus": "^7.2.0",
"jsonlint": "^1.6.3",
"jsonpath-plus": "^7.2.0",
"know-your-http-well": "^0.5.0",
"lodash": "^4.17.21",
"markdown-it": "^13.0.2",
@@ -66,6 +67,7 @@
"react-dom": "18.2.0",
"react-github-btn": "^1.4.0",
"react-hot-toast": "^2.4.0",
"react-i18next": "^15.0.1",
"react-inspector": "^6.0.2",
"react-pdf": "^7.5.1",
"react-redux": "^7.2.6",

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { IconSpeakerphone, IconBrandTwitter, IconBrandGithub, IconBrandDiscord, IconBook } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
import { useDictionary } from 'providers/Dictionary/index';
import { useTranslation } from 'react-i18next';
const Support = () => {
const { dictionary } = useDictionary();
const { t } = useTranslation();
return (
<StyledWrapper>
@@ -12,31 +12,31 @@ const Support = () => {
<div className="mt-2">
<a href="https://docs.usebruno.com" target="_blank" className="flex items-end">
<IconBook size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.documentation}</span>
<span className="label ml-2">{t('COMMON.DOCUMENTATION')}</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="flex items-end">
<IconSpeakerphone size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.reportIssues}</span>
<span className="label ml-2">{t('COMMON.REPORT_ISSUES')}</span>
</a>
</div>
<div className="mt-2">
<a href="https://discord.com/invite/KgcZUncpjq" target="_blank" className="flex items-end">
<IconBrandDiscord size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.discord}</span>
<span className="label ml-2">{t('COMMON.DISCORD')}</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-end">
<IconBrandGithub size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.gitHub}</span>
<span className="label ml-2">{t('COMMON.GITHUB')}</span>
</a>
</div>
<div className="mt-2">
<a href="https://twitter.com/use_bruno" target="_blank" className="flex items-end">
<IconBrandTwitter size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.twitter}</span>
<span className="label ml-2">{t('COMMON.TWITTER')}</span>
</a>
</div>
</div>

View File

@@ -1,6 +1,7 @@
import { useState } from 'react';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { openCollection, importCollection } from 'providers/ReduxStore/slices/collections/actions';
import { IconBrandGithub, IconPlus, IconDownload, IconFolders, IconSpeakerphone, IconBook } from '@tabler/icons';
@@ -9,11 +10,10 @@ import CreateCollection from 'components/Sidebar/CreateCollection';
import ImportCollection from 'components/Sidebar/ImportCollection';
import ImportCollectionLocation from 'components/Sidebar/ImportCollectionLocation';
import StyledWrapper from './StyledWrapper';
import { useDictionary } from 'providers/Dictionary/index';
const Welcome = () => {
const dispatch = useDispatch();
const { dictionary } = useDictionary();
const { t } = useTranslation();
const [importedCollection, setImportedCollection] = useState(null);
const [importedTranslationLog, setImportedTranslationLog] = useState({});
const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false);
@@ -22,7 +22,7 @@ const Welcome = () => {
const handleOpenCollection = () => {
dispatch(openCollection()).catch(
(err) => console.log(err) && toast.error(dictionary.errorWhileOpeningCollection)
(err) => console.log(err) && toast.error(t('WELCOME.COLLECTION_OPEN_ERROR'))
);
};
@@ -40,12 +40,12 @@ const Welcome = () => {
.then(() => {
setImportCollectionLocationModalOpen(false);
setImportedCollection(null);
toast.success(dictionary.collectionImportedSuccessfully);
toast.success(t('WELCOME.COLLECTION_IMPORT_SUCCESS'));
})
.catch((err) => {
setImportCollectionLocationModalOpen(false);
console.error(err);
toast.error(dictionary.errorWhileImportingCollection);
toast.error(t('WELCOME.COLLECTION_IMPORT_ERROR'));
});
};
@@ -68,45 +68,45 @@ const Welcome = () => {
<Bruno width={50} />
</div>
<div className="text-xl font-semibold select-none">bruno</div>
<div className="mt-4">{dictionary.aboutBruno}</div>
<div className="mt-4">{t('WELCOME.ABOUT_BRUNO')}</div>
<div className="uppercase font-semibold heading mt-10">{dictionary.collections}</div>
<div className="uppercase font-semibold heading mt-10">{t('COMMON.COLLECTIONS')}</div>
<div className="mt-4 flex items-center collection-options select-none">
<div className="flex items-center" onClick={() => setCreateCollectionModalOpen(true)}>
<IconPlus size={18} strokeWidth={2} />
<span className="label ml-2" id="create-collection">
{dictionary.createCollection}
{t('WELCOME.CREATE_COLLECTION')}
</span>
</div>
<div className="flex items-center ml-6" onClick={handleOpenCollection}>
<IconFolders size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.openCollection}</span>
<span className="label ml-2">{t('WELCOME.OPEN_COLLECTION')}</span>
</div>
<div className="flex items-center ml-6" onClick={() => setImportCollectionModalOpen(true)}>
<IconDownload size={18} strokeWidth={2} />
<span className="label ml-2" id="import-collection">
{dictionary.importCollection}
{t('WELCOME.IMPORT_COLLECTION')}
</span>
</div>
</div>
<div className="uppercase font-semibold heading mt-10 pt-6">Links</div>
<div className="uppercase font-semibold heading mt-10 pt-6">{t('WELCOME.LINKS')}</div>
<div className="mt-4 flex flex-col collection-options select-none">
<div className="flex items-center mt-2">
<a href="https://docs.usebruno.com" target="_blank" className="inline-flex items-center">
<IconBook size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.documentation}</span>
<span className="label ml-2">{t('COMMON.DOCUMENTATION')}</span>
</a>
</div>
<div className="flex items-center mt-2">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="inline-flex items-center">
<IconSpeakerphone size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.reportIssues}</span>
<span className="label ml-2">{t('COMMON.REPORT_ISSUES')}</span>
</a>
</div>
<div className="flex items-center mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-center">
<IconBrandGithub size={18} strokeWidth={2} />
<span className="label ml-2">{dictionary.gitHub}</span>
<span className="label ml-2">{t('COMMON.GITHUB')}</span>
</a>
</div>
</div>

View File

@@ -1,16 +0,0 @@
export default {
aboutBruno: 'Opensource IDE for exploring and testing APIs',
collections: 'Collections',
createCollection: 'Create Collection',
openCollection: 'Open Collection',
importCollection: 'Import Collection',
documentation: 'Documentation',
reportIssues: 'Report Issues',
gitHub: 'GitHub',
collectionImportedSuccessfully: 'Collection imported successfully',
errorWhileOpeningCollection: 'An error occurred while opening the collection',
errorWhileImportingCollection:
'An error occurred while importing the collection. Check the logs for more information.',
discord: 'Discord',
twitter: 'Twitter'
};

View File

@@ -1,5 +0,0 @@
import en from './en.js';
export const dictionaries = {
en
};

View File

@@ -0,0 +1,24 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import translationEn from './translation/en.json';
const resources = {
en: {
translation: translationEn,
},
};
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources,
lng: 'en', // Use "en" as the default language. "cimode" can be used to debug / show translation placeholder
ns: 'translation', // Use translation as the default Namespace that will be loaded by default
interpolation: {
escapeValue: false // react already safes from xss
}
});
export default i18n;

View File

@@ -0,0 +1,20 @@
{
"COMMON": {
"COLLECTIONS": "Collections",
"DOCUMENTATION": "Documentation",
"REPORT_ISSUES": "Report Issues",
"GITHUB": "GitHub",
"DISCORD": "Discord",
"TWITTER": "Twitter"
},
"WELCOME": {
"ABOUT_BRUNO": "Opensource IDE for exploring and testing APIs",
"LINKS": "Links",
"CREATE_COLLECTION": "Create Collection",
"OPEN_COLLECTION": "Open Collection",
"IMPORT_COLLECTION": "Import Collection",
"COLLECTION_IMPORT_SUCCESS": "Collection imported successfully",
"COLLECTION_IMPORT_ERROR": "An error occurred while importing the collection. Check the logs for more information.",
"COLLECTION_OPEN_ERROR": "An error occurred while opening the collection"
}
}

View File

@@ -23,7 +23,6 @@ import '@fontsource/inter/600.css';
import '@fontsource/inter/700.css';
import '@fontsource/inter/800.css';
import '@fontsource/inter/900.css';
import { DictionaryProvider } from 'providers/Dictionary/index';
function SafeHydrate({ children }) {
return <div suppressHydrationWarning>{typeof window === 'undefined' ? null : children}</div>;
@@ -69,15 +68,13 @@ function MyApp({ Component, pageProps }) {
<NoSsr>
<Provider store={ReduxStore}>
<ThemeProvider>
<DictionaryProvider>
<ToastProvider>
<AppProvider>
<HotkeysProvider>
<Component {...pageProps} />
</HotkeysProvider>
</AppProvider>
</ToastProvider>
</DictionaryProvider>
<ToastProvider>
<AppProvider>
<HotkeysProvider>
<Component {...pageProps} />
</HotkeysProvider>
</AppProvider>
</ToastProvider>
</ThemeProvider>
</Provider>
</NoSsr>

View File

@@ -1,6 +1,7 @@
import Head from 'next/head';
import Bruno from './Bruno';
import GlobalStyle from '../globalStyles';
import '../i18n';
export default function Home() {
return (

View File

@@ -1,28 +0,0 @@
import React from 'react';
import { useState, useContext } from 'react';
import { dictionaries } from 'src/dictionaries/index';
export const DictionaryContext = React.createContext();
const DictionaryProvider = (props) => {
const [language, setLanguage] = useState('en');
const dictionary = dictionaries[language] ?? dictionaries.en;
return (
<DictionaryContext.Provider {...props} value={{ language, setLanguage, dictionary }}>
<>{props.children}</>
</DictionaryContext.Provider>
);
};
const useDictionary = () => {
const context = useContext(DictionaryContext);
if (context === undefined) {
throw new Error(`useDictionary must be used within a DictionaryProvider`);
}
return context;
};
export { useDictionary, DictionaryProvider };