Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reducing the size of locales that will be downloaded on page load by specifying namespaces #321

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/next-pages/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export default {
'missing.translation.in.fr': 'This should work',
'cows#one': 'A cow',
'cows#other': '{count} cows',
'scope2.test': 'A scope',
'scope2.more.test': 'A more scoped ',
} as const;

// We can also write locales using nested objects
Expand Down
21 changes: 21 additions & 0 deletions examples/next-pages/pages/ssr-ssg-scoped.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { GetServerSideProps } from 'next';
import { getLocaleProps, useCurrentLocale, useI18n } from '../locales';

export const getServerSideProps: GetServerSideProps = getLocaleProps(['scope2']);
// export const getStaticProps: GetStaticProps = getLocaleProps();

export default function SSR() {
const t = useI18n();
const locale = useCurrentLocale();

return (
<div>
<h1>SSR / SSG</h1>
<p>
Current locale: <span>{locale}</span>
</p>
<p>Hello: {t('scope2.test')}</p>
<p>Hello: {t('scope2.more.test')}</p>
</div>
);
}
52 changes: 51 additions & 1 deletion packages/next-international/__tests__/get-locale-props.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('getLocaleProps', () => {
fr: () => import('./utils/fr'),
});

const props = await getLocaleProps(() => ({
const props = await getLocaleProps(undefined, () => ({
props: {
hello: 'world',
},
Expand All @@ -66,3 +66,53 @@ describe('getLocaleProps', () => {
});
});
});

it('should return scoped locale with when scope defined in params getStaticProps', async () => {
const { getLocaleProps } = createI18n({
en: () => import('./utils/en'),
fr: () => import('./utils/fr'),
});

const props = await getLocaleProps(['namespace'])({
locale: 'en',
defaultLocale: 'en',
locales: ['en', 'fr'],
});

expect(props).toEqual({
props: {
locale: {
'namespace.hello': 'Hello',
'namespace.subnamespace.hello': 'Hello',
'namespace.subnamespace.hello.world': 'Hello World!',
'namespace.subnamespace.weather': "Today's weather is {weather}",
'namespace.subnamespace.user.description': '{name} is {years} years old',
},
},
});

it('should return scoped locale with when scope defined in params getStaticProps', async () => {
const { getLocaleProps } = createI18n({
en: () => import('./utils/en'),
fr: () => import('./utils/fr'),
});

const props = await getLocaleProps(['namespace.subnamespace'])({
locale: 'en',
defaultLocale: 'en',
locales: ['en', 'fr'],
});

expect(props).toEqual({
props: {
locale: {
'namespace.subnamespace.hello': 'Hello',
'namespace.subnamespace.hello.world': 'Hello World!',
'namespace.subnamespace.weather': "Today's weather is {weather}",
'namespace.subnamespace.user.description': '{name} is {years} years old',
locale: en,
},
},
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { BaseLocale, Scopes } from 'international-types';

export const filterLocalesByNameSpace = <Locale extends BaseLocale, Scope extends Scopes<Locale>>(
locale: Locale,
scopes?: Scope[],
): Locale => {
if (!scopes?.length) return locale;

return Object.entries(locale).reduce((prev, [name, value]) => {
if (scopes.some(scope => name.startsWith(scope))) {
const k = name as keyof Locale;
prev[k] = value as Locale[string];
}

return prev;
}, {} as Locale);
};
11 changes: 7 additions & 4 deletions packages/next-international/src/pages/create-get-locale-props.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { ImportedLocales } from 'international-types';
import type { BaseLocale, ImportedLocales, Scopes } from 'international-types';
import type { GetStaticProps, GetServerSideProps } from 'next';
import { error } from '../helpers/log';
import { flattenLocale } from '../common/flatten-locale';
import { filterLocalesByNameSpace } from '../common/filter-locales-by-namespace';

export function createGetLocaleProps(locales: ImportedLocales) {
return function getLocaleProps<
T extends { [key: string]: any },
GetProps extends GetStaticProps<T> | GetServerSideProps<T>,
>(initialGetProps?: GetProps) {
Scope extends Scopes<BaseLocale>,
>(scopes?: Scope[], initialGetProps?: GetProps) {
return async (context: any) => {
const initialResult = await initialGetProps?.(context);

Expand All @@ -18,13 +20,14 @@ export function createGetLocaleProps(locales: ImportedLocales) {
}

const load = locales[context.locale];

const allLocale = flattenLocale((await load()).default);
const scopedLocale = filterLocalesByNameSpace(allLocale, scopes);
return {
...initialResult,
props: {
// @ts-expect-error Next `GetStaticPropsResult` doesn't have `props`
...initialResult?.props,
locale: flattenLocale((await load()).default),
locale: scopedLocale,
},
};
};
Expand Down