Skip to content

Commit

Permalink
Merge pull request #5 from aypineau/feat--implement-useTranslation-in…
Browse files Browse the repository at this point in the history
…to-nextjs-metadata

feat/implement typing translation
  • Loading branch information
aymericzip authored Apr 19, 2024
2 parents 418d209 + 7dd5c92 commit ad4ba2f
Show file tree
Hide file tree
Showing 25 changed files with 299 additions and 83 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:

concurrency: ${{ github.workflow }}-${{ github.ref }}

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
build:
# github.actor!= 'github-actions' -> Ignore the pull request which comes from user depbot.
Expand Down
26 changes: 26 additions & 0 deletions examples/nextjs-app/src/app/[locale]/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getTranslationContent } from 'intlayer';
import type { Metadata } from 'next';
import type { LocalParams } from 'next-intlayer';

export const generateMetadata = ({
params: { locale },
}: LocalParams): Metadata => {
return {
title: getTranslationContent<string>(
{
en: 'page title',
fr: 'titre de la page',
es: 'título de la página',
},
locale
),
description: getTranslationContent(
{
en: 'page description',
es: 'descripción de la página',
fr: '',
},
locale
),
};
};
3 changes: 3 additions & 0 deletions examples/nextjs-app/src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import type { NextPageIntlayer } from 'next-intlayer';
import { LocaleClientContextProvider } from 'next-intlayer/client';
import { useIntlayer, LocaleServerContextProvider } from 'next-intlayer/server';

import { generateMetadata } from './metadata';
export { generateMetadata };

const Page: NextPageIntlayer = ({ params: { locale } }) => {
// Because the page is not wrapped in the LocaleServerContextProvider, we need to use add the locale to the useIntlayer hook
const content = useIntlayer('page', locale);
Expand Down
5 changes: 3 additions & 2 deletions packages/@intlayer/config/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export type {
BaseContentConfig as NotDerivedConfiguration,
BaseDerivedConfig as BaseDirDerivedConfiguration,
ResultDirDerivedConfig as ResultDirDerivedConfiguration,
} from './types';
export { Locales } from './defaultValues/locales';
} from './types/config';
export type { LocalesValues } from './types/locales';
export { Locales } from './types/locales';
export {
getConfiguration,
intlayerConfiguration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type {
MiddlewareConfig,
BaseContentConfig,
ResultDirDerivedConfig,
} from '../types';
} from '../types/config';
import type { GetConfigurationOptions } from './getConfiguration';

let storedConfiguration: IntlayerConfig;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { relative } from 'path';
import type { CustomIntlayerConfig, IntlayerConfig } from '../types';
import type { CustomIntlayerConfig, IntlayerConfig } from '../types/config';
import { buildConfigurationFields } from './buildConfigurationFields';
import { loadConfigurationFile } from './loadConfigurationFile';
import { searchConfigurationFile } from './searchConfigurationFile';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { createRequire } from 'module';
import { type Context, runInNewContext } from 'vm';
import { type BuildOptions, buildSync, type BuildResult } from 'esbuild';
import type { CustomIntlayerConfig } from '../types';
import type { CustomIntlayerConfig } from '../types/config';

const isESModule = typeof import.meta.url === 'string';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Locales } from './locales';
import { Locales } from '../types/locales';

export const LOCALES: Locales[] = [
Locales.ENGLISH,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Locales } from '../defaultValues/locales';
import type {
ContentConfig,
InternationalizationConfig,
IntlayerConfig,
MiddlewareConfig,
ServerSetCookieRule,
} from '../types';
} from '../types/config';
import type { Locales } from '../types/locales';
import { getEnvValue } from './utils';

export const intlayerIntlConfiguration: InternationalizationConfig = {
Expand Down
5 changes: 3 additions & 2 deletions packages/@intlayer/config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export type {
PatternsContentConfig,
ContentConfig,
IntlayerConfig,
} from './types';
export { Locales } from './defaultValues/locales';
} from './types/config';
export type { LocalesValues } from './types/locales';
export { Locales } from './types/locales';
export {
formatEnvVariable,
intlayerIntlConfiguration,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Locales } from '../defaultValues/locales';
import type { Locales } from './locales';

export type InternationalizationConfig = {
// Available languages in the app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,9 @@ export enum Locales {
// Zulu language as used in South Africa
ZULU_SOUTH_AFRICA = 'zu-ZA',
}

// Utility type to extract the values from an enum
type ValueOf<T> = T[keyof T];

// Define MyType using the ValueOf utility type on Locales
export type LocalesValues = ValueOf<typeof Locales>;
30 changes: 30 additions & 0 deletions packages/@intlayer/core/@types/intlayere.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// /* eslint-disable */
// import 'intlayer';
// import { Locales } from '@intlayer/config';
// import type { ServerComponentExampleContent as _TxBivAmuhkFBKLSJ03g4 } from '../.intlayer/types/server-component-example.d.ts';
// import type { PageContent as _PBE9SfN3Jt6Sjy1nlqxG } from '../.intlayer/types/page.d.ts';
// import type { NestedServerComponentExampleContent as _rBCBdYFsnB0FVPPSPK97 } from '../.intlayer/types/nested-server-component-example.d.ts';
// import type { LangSwitcherContent as _PNzYE5o0D8MsoRVAp0uu } from '../.intlayer/types/lang-switcher.d.ts';
// import type { EnumerationContent as _14pNbgQIFpuN7gw8mHyP } from '../.intlayer/types/enumeration.d.ts';
// import type { ClientComponentExampleContent as _XbCoKLjxSnL8cxPxyS5z } from '../.intlayer/types/client-component-example.d.ts';

// export {};

// declare module 'intlayer' {
// interface IntlayerDictionaryTypesConnector {
// 'server-component-example': _TxBivAmuhkFBKLSJ03g4;
// page: _PBE9SfN3Jt6Sjy1nlqxG;
// 'nested-server-component-example': _rBCBdYFsnB0FVPPSPK97;
// 'lang-switcher': _PNzYE5o0D8MsoRVAp0uu;
// enumeration: _14pNbgQIFpuN7gw8mHyP;
// 'client-component-example': _XbCoKLjxSnL8cxPxyS5z;
// }

// enum ConfigLocales {
// ENGLISH = 'en',
// FRENCH = 'fr',
// SPANISH = 'es',
// }

// interface IConfigLocales<Content> extends Record<ConfigLocales, Content> {}
// }
2 changes: 2 additions & 0 deletions packages/@intlayer/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export type {
CustomizableLanguageContent,
LanguageContent,
TranslationContent,
EnumerationContent,
QuantityContent,
CustomLocales as Locales,
} from './transpiler/content_transformers/index';
export {
t,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import {
type Locales,
intlayerIntlConfiguration,
} from '@intlayer/config/client';
import type { LanguageContent } from './translations';
import type { CustomizableLanguageContent } from './types';

const defaultLocale = intlayerIntlConfiguration.defaultLocale;

export const getTranslationContent = <Content>(
languageContent: LanguageContent<Content>,
type GetTranslationContent = <Content = string>(
languageContent: CustomizableLanguageContent<Content>,
locale: Locales
): Content =>
languageContent[locale ?? defaultLocale] ?? languageContent[defaultLocale];
) => Content;

export const getTranslationContent: GetTranslationContent = <Content = string>(
languageContent: CustomizableLanguageContent<Content>,
locale: Locales
) => languageContent[locale ?? defaultLocale] ?? languageContent[defaultLocale];
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './translations';
export * from './translation';
export * from './getTranslationContent';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { intlayerConfiguration } from '@intlayer/config/client';
import { NodeType } from '../../../types/index';
import { getStackTraceInfo } from '../../../utils/getStackTraceInfo';
import type { CustomizableLanguageContent, TranslationContent } from './types';

const { defaultLocale } = intlayerConfiguration.internationalization;

/**
* Transpile multilingual dictionary content
*
* Usage
*
* translation<string>({
* "en": "test",
* "fr": "test",
* // ... any other available locale
* })
*
* translation<number, Locales.ENGLISH>({
* "en": 1
* })
*
* translation<string, Locales.ENGLISH | Locales.FRENCH>({
* "fr": "test",
* "en": "test"
* })
*
*/
const translation = <Content = string>(
content?: CustomizableLanguageContent<Content>
) => {
const stackTraceInfo = getStackTraceInfo();

if (typeof content === 'string') {
const result: TranslationContent<Content> = {
nodeType: NodeType.Translation,
...stackTraceInfo,
[defaultLocale]: content,
} as TranslationContent<Content>;

return result;
}

const result: TranslationContent<Content> = {
nodeType: NodeType.Translation,
...stackTraceInfo,
...(content as unknown as object),
} as TranslationContent<Content>;

return result;
};

export { translation as t };

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { LocalesValues } from '@intlayer/config/client';
// @ts-expect-error intlayer declared for module augmentation
import type { IConfigLocales } from 'intlayer';
import type { NodeType } from '../../../types';
import type { NoteStackTraceInfo } from '../../../utils/getStackTraceInfo';

/**
* If module augmented, it will return the configured locales such as Locales.ENGLISH | Locales.FRENCH | Locales.SPANISH | ...
* If not, it will return never
*/
export type ConfigLocales = keyof IConfigLocales<unknown>;

/**
* If module augmented, it will return the configured locales such as Locales.ENGLISH | Locales.FRENCH | Locales.SPANISH | ...
* If not, it will return all available locales such as Locales.ENGLISH | Locales.FRENCH | Locales.SPANISH | ...
*/
export type CustomLocales = ConfigLocales extends never
? LocalesValues
: ConfigLocales;

/**
* Record of locales and content
*
* const myVar1: LanguageContent<string> = {
* "en": "",
* "fr": ""
* }
*
* const myVar2: LanguageContent<{age: number, name: string}> = {
* "en": {age: 1, name: "test"},
* "fr": {age: 1, name: "test"}
* }
*/
export type LanguageContent<Content> = Partial<Record<LocalesValues, Content>>;
export type ConfigLanguageContent<Content> = Record<ConfigLocales, Content>;

/**
* Valid
* const test: CustomizableLanguageContent<string, Locales.ENGLISH | Locales.FRENCH> = {
* "en": "test",
* "fr": "test"
* }
*
* const test: CustomizableLanguageContent<number> = {
* "fr": 1,
* "en": 1,
* ... any other available locale
* }
*
* Invalid
*
* const test: CustomizableLanguageContent<string> = {
* "en": "test",
* "fr": "test",
* "sss": "test" // Does not exist in Locales
* }
*
* const test: CustomizableLanguageContent<string, Locales.ENGLISH | Locales.FRENCH> = {
* "fr": "test"
* // Locales.ENGLISH is missing
* }
*
*/
export type CustomizableLanguageContent<Content = string> =
ConfigLocales extends never
? LanguageContent<Content>
: ConfigLanguageContent<Content>;

export type TranslationContent<Content> = LanguageContent<Content> &
NoteStackTraceInfo & {
nodeType: NodeType.Translation;
};
8 changes: 1 addition & 7 deletions packages/@intlayer/core/src/transpiler/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
export type {
LanguageContent,
TranslationContent,
EnumerationContent,
QuantityContent,
} from './content_transformers/index';
export { t, enu } from './content_transformers/index';
export * from './content_transformers/index';
Loading

0 comments on commit ad4ba2f

Please sign in to comment.