diff --git a/examples/next-pages/locales/en.ts b/examples/next-pages/locales/en.ts index 272422a..16712c4 100644 --- a/examples/next-pages/locales/en.ts +++ b/examples/next-pages/locales/en.ts @@ -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 diff --git a/examples/next-pages/pages/ssr-ssg-scoped.tsx b/examples/next-pages/pages/ssr-ssg-scoped.tsx new file mode 100644 index 0000000..41af0bb --- /dev/null +++ b/examples/next-pages/pages/ssr-ssg-scoped.tsx @@ -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 ( +
+

SSR / SSG

+

+ Current locale: {locale} +

+

Hello: {t('scope2.test')}

+

Hello: {t('scope2.more.test')}

+
+ ); +} diff --git a/packages/next-international/__tests__/get-locale-props.test.ts b/packages/next-international/__tests__/get-locale-props.test.ts index 01f73bb..bc17e6f 100644 --- a/packages/next-international/__tests__/get-locale-props.test.ts +++ b/packages/next-international/__tests__/get-locale-props.test.ts @@ -48,7 +48,7 @@ describe('getLocaleProps', () => { fr: () => import('./utils/fr'), }); - const props = await getLocaleProps(() => ({ + const props = await getLocaleProps(undefined, () => ({ props: { hello: 'world', }, @@ -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, + }, + }, + }); + }); +}); diff --git a/packages/next-international/src/common/filter-locales-by-namespace.ts b/packages/next-international/src/common/filter-locales-by-namespace.ts new file mode 100644 index 0000000..a391159 --- /dev/null +++ b/packages/next-international/src/common/filter-locales-by-namespace.ts @@ -0,0 +1,17 @@ +import type { BaseLocale, Scopes } from 'international-types'; + +export const filterLocalesByNameSpace = >( + 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); +}; diff --git a/packages/next-international/src/pages/create-get-locale-props.ts b/packages/next-international/src/pages/create-get-locale-props.ts index bc87462..e0e88c8 100644 --- a/packages/next-international/src/pages/create-get-locale-props.ts +++ b/packages/next-international/src/pages/create-get-locale-props.ts @@ -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 | GetServerSideProps, - >(initialGetProps?: GetProps) { + Scope extends Scopes, + >(scopes?: Scope[], initialGetProps?: GetProps) { return async (context: any) => { const initialResult = await initialGetProps?.(context); @@ -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, }, }; };