Merge pull request #51 from datopian/feature/i18n-next10

Configure i18n for Portaljs
This commit is contained in:
Anuar Ustayev 2020-12-15 17:32:33 +06:00 committed by GitHub
commit 427d9f5c40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 6892 additions and 3683 deletions

View File

@ -1,8 +1,8 @@
<h1 align="center">
🌀 Portal.JS<br/>
The javascript framework for<br/>
data portals
🌀 Portal.JS<br/>
The javascript framework for<br/>
data portals
</h1>
@ -102,6 +102,61 @@ Note that we don't have Apollo Server but we connect CKAN API using [`apollo-lin
For development/debugging purposes, we suggest installing the Chrome extension - https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm.
#### i18n configuration
Portal.js is configured by default to support both `English` and `French` subpath for language translation. But for subsequent users, this following steps can be used to configure i18n for other languages;
1. Update ` next.config.js`, to add more languages to the i18n locales
```js
i18n: {
locales: ['en', 'fr', 'nl-NL'], // add more language to the list
defaultLocale: 'en', // set the default language to use
},
```
2. Create a folder for the language in `locales` --> `locales/en-Us`
3. In the language folder, different namespace files (json) can be created for each translation. For the `index.js` use-case, I named it `common.json`
```json
// locales/en/common.json
{
"title" : "Portal js in English",
}
// locales/fr/common.json
{
"title" : "Portal js in French",
}
```
4. To use on pages using Server-side Props.
```js
import { loadNamespaces } from './_app';
import useTranslation from 'next-translate/useTranslation';
const Home: React.FC = ()=> {
const { t } = useTranslation();
return (
<div>{t(`common:title`)}</div> // we use common and title base on the common.json data
);
};
export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
........ ........
return {
props : {
_ns: await loadNamespaces(['common'], locale),
}
};
};
```
5. Go to the browser and view the changes using language subpath like this `http://localhost:3000` and `http://localhost:3000/fr`. **Note** The subpath also activate chrome language Translator
#### Pre-fetch data in the server-side
When visiting a dataset page, you may want to fetch the dataset metadata in the server-side. To do so, you can use `getServerSideProps` function from NextJS:

View File

@ -16,7 +16,7 @@ module.exports = {
'<rootDir>/postcss.config.js',
],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': '<rootDir>/node_modules/babel-jest',
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
'^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
},
transformIgnorePatterns: [

View File

@ -0,0 +1,4 @@
{
"title": "Portal in English",
"description": "At Datahub, we have over thousands of datasets for free and a Premium Data Service for additional or customised data with guaranteed updates."
}

View File

@ -0,0 +1,4 @@
{
"title": "Portal in french",
"description": "Chez Datahub, nous avons plus de milliers d'ensembles de données gratuitement et un service de données Premium pour des données supplémentaires ou personnalisées avec des mises à jour garanties."
}

View File

@ -25,6 +25,10 @@ module.exports = (phase, { defaultConfig }) => {
}
return {
i18n: {
locales: ['en', 'fr', 'nl-NL'],
defaultLocale: 'en',
},
publicRuntimeConfig: {
DMS: dms ? dms.replace(/\/?$/, '') : 'http://mock.ckan',
CMS: cms ? cms.replace(/\/?$/, '') : 'oddk.home.blog',
@ -32,6 +36,10 @@ module.exports = (phase, { defaultConfig }) => {
};
}
return {
i18n: {
locales: ['en', 'fr', 'nl-NL'],
defaultLocale: 'en',
},
publicRuntimeConfig: {
DMS: dms ? dms.replace(/\/?$/, '') : 'https://demo.ckan.org',
CMS: cms ? cms.replace(/\/?$/, '') : 'oddk.home.blog',

5321
packages/portal/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,8 @@
"graphql-tag": "^2.10.3",
"html-react-parser": "^0.13.0",
"markdown-it": "^11.0.0",
"next": "9.4.2",
"next": "^10.0.3",
"next-translate": "^0.20.2",
"qs": "^6.9.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",

View File

@ -3,9 +3,27 @@ import { ApolloProvider } from '@apollo/react-hooks';
import { useApollo } from '../lib/apolloClient';
import { DEFAULT_THEME } from '../themes';
import { applyTheme } from '../themes/utils';
import I18nProvider from 'next-translate/I18nProvider';
import { useRouter } from 'next/router';
import '../styles/app.css';
interface I8nObject {
[property: string]: any;
}
export async function loadNamespaces(
namespaces: string[],
lang: string
): Promise<I8nObject> {
const res = {};
for (const ns of namespaces) {
res[ns] = await import(`../locales/${lang}/${ns}.json`).then(
(m) => m.default
);
}
return res;
}
type Props = {
Component: any;
pageProps: any;
@ -14,6 +32,7 @@ type Props = {
const MyApp: React.FC<Props> = ({ Component, pageProps }) => {
const apolloClient = useApollo(pageProps.initialApolloState);
const [theme] = useState(DEFAULT_THEME); // setTheme
const router = useRouter();
useEffect(() => {
/**
@ -25,9 +44,11 @@ const MyApp: React.FC<Props> = ({ Component, pageProps }) => {
}, [theme]);
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
<I18nProvider lang={router.locale} namespaces={pageProps._ns}>
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
</I18nProvider>
);
};

View File

@ -5,36 +5,48 @@ import Nav from '../components/home/Nav';
import Recent from '../components/home/Recent';
import Form from '../components/search/Form';
import { SEARCH_QUERY } from '../graphql/queries';
import { loadNamespaces } from './_app';
import useTranslation from 'next-translate/useTranslation';
const Home: React.FC = () => (
<div className="container mx-auto">
<Head>
<title>Portal</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Nav />
<section className="flex justify-center items-center flex-col mt-8 mx-4 lg:flex-row">
<div>
<h1 className="text-4xl mb-3 font-thin">
Find, Share and Publish <br /> Quality Data with{' '}
<span className="text-orange-500">Datahub</span>
</h1>
<p className="text-md font-light mb-3 w-4/5">
At Datahub, we have over thousands of datasets for free and a Premium
Data Service for additional or customised data with guaranteed
updates.
</p>
<Form />
</div>
<div className="mt-4">
<img src="/images/banner.svg" className="w-4/5" alt="banner_img" />
</div>
</section>
<Recent />
</div>
);
const Home: React.FC<{ locale: any; locales: any }> = ({
locale,
locales,
}) => {
const { t } = useTranslation();
export const getServerSideProps: GetServerSideProps = async () => {
return (
<>
<div className="container mx-auto">
<Head>
<title>{t(`common:title`)}</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Nav />
<section className="flex justify-center items-center flex-col mt-8 mx-4 lg:flex-row">
<div>
<h1 className="text-4xl mb-3 font-thin">
Find, Share and Publish <br /> Quality Data with{' '}
<span className="text-orange-500">Datahub</span>
</h1>
<p className="text-md font-light mb-3 w-4/5">
{t(`common:description`)}
</p>
<Form />
</div>
<div className="mt-4">
<img src="/images/banner.svg" className="w-4/5" alt="banner_img" />
</div>
</section>
<Recent />
</div>
</>
);
};
export const getServerSideProps: GetServerSideProps = async ({
locale,
locales,
}) => {
const apolloClient = initializeApollo();
await apolloClient.query({
@ -48,6 +60,9 @@ export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
initialApolloState: apolloClient.cache.extract(),
_ns: await loadNamespaces(['common'], locale),
locale,
locales,
},
};
};

File diff suppressed because it is too large Load Diff

2493
yarn.lock

File diff suppressed because it is too large Load Diff