npm package: @fengcaic/react-locale
Description: Internationalize Elegant intl library based for Reactjs
Latest version: 4.2.0

# Fengcaic React Locale

<p align="center">
    Internationalize Elegant intl library based for Reactjs
</p>

<div align="center">

[![NPM](https://img.shields.io/npm/v/@fengcaic/react-locale.svg?style=for-the-badge)](https://www.npmjs.com/package/@fengcaic/react-locale)
[![npm](https://img.shields.io/bundlejs/size/@fengcaic/react-locale?style=for-the-badge)](https://github.com/fengcaic/@fengcaic/react-locale/blob/main/LICENSE)
[![npm](https://img.shields.io/npm/l/@fengcaic/react-locale?style=for-the-badge)](https://github.com/fengcaic/react-locale/blob/main/LICENSE)

[![npm downloads](https://img.shields.io/npm/dm/@fengcaic/react-locale.svg?style=for-the-badge)](https://www.npmjs.com/package/@fengcaic/react-locale)
[![npm](https://img.shields.io/npm/dt/@fengcaic/react-locale.svg?style=for-the-badge)](https://www.npmjs.com/package/@fengcaic/react-locale)


</div>


`^3.2.1 support react >=18.0.0 <20.0.0`


## Features

- Use `react-intl v7` for packaging
- Support Hooks
- Support `Nextjs 15+`


## Install

```bash
yarn add @fengcaic/react-locale
```

## Usage

create src/config/locale.ts

```tsx
import {TLocaleDictionaries} from '@fengcaic/react-locale';
import {enUS} from '@/locales/en-US';
import {zhTW} from '@/locales/zh-TW';
import {jaJP} from '@/locales/ja-JP';

export enum ELocales {
    enUS = 'en-US',
    zhTW = 'zh-TW',
    jaJP = 'ja-JP',
}

export const localeDictionaries: TLocaleDictionaries = {
    [ELocales.enUS]: enUS,
    [ELocales.zhTW]: zhTW,
    [ELocales.jaJP]: jaJP,
};
```

create src/locales/en-US.ts (Default Locales)

```ts
/*-------------------------
*      Default Locales
---------------------------*/
const translations = {
    page: {
        'page.home.field.title': 'Title',
        'page.home.desc': `This is a carousel developed directly using React + Flexbox (non-js secondary development package into React),
    and only include the features you need, not too many cool effects that might complicate your usage or create other weird issues.`,
        'page.home.feature.title': 'Feature',
    },
    formatError:{
        'formatError.email': 'Email format error',
        'formatError.max': 'The maximum value is {max}',
        'formatError.maxFraction': 'The floating-point number can only have a maximum of {maxFraction} digits',
        'formatError.maxLength': 'Up to {maxLength} characters',
        'formatError.min': 'The minimum value is {min}',
        'formatError.minLength': 'At least {minLength} characters',
        'formatError.pattern': 'Format error',
        'formatError.required': 'Required Column'
    }
};

export default translations;
export type TDefaultTranslations = typeof translations;
```

create src/locales/zh-TW.ts (Options Locales)

```ts
import {TDefaultTranslations} from './en-US'; // Import default locales

const translations: TDefaultTranslations = {
    page:{
        'page.home.field.title': '標題',
        'page.home.desc': `Bear Carousel 是一個直接使用React + Flexbox開發的輪播套件 (非js二次開發包成React)，
     並且只包含你需要的功能，沒有太多很酷的效果，因為那些可能會讓你變得不容易使用並且產生其他奇怪的問題`,
        'page.home.feature.title': '特性',
    },
    formatError: {
        'formatError.email': '電子郵件格式錯誤',
        'formatError.max': '最大值為 {max}',
        'formatError.maxFraction': '浮點數最多僅能為 {maxFraction} 位數',
        'formatError.maxLength': '最多 {maxLength} 個字元',
        'formatError.min': '最小值為 {min}',
        'formatError.minLength': '至少 {minLength} 個字元',
        'formatError.pattern': '格式錯誤',
        'formatError.required': '必填欄位'
    }
} as const;

export default translations;
```

> as const only other language


create src/library/react-locale/index.tsx

```tsx
import {BlockWrapper} from '@fengcaic/react-block';
import {LocaleProvider} from '@fengcaic/react-locale';
import dayjs from 'dayjs';
import React from 'react';
import styled from 'styled-components';

import {DEFAULT_LOCALE, serverDictionaries} from '@/config/locale';
import {persistKey} from '@/config/app';

interface IProps {
    children: JSX.Element
}


/**
 * Custom Locale Provider
 * @param children
 */
const ReactLocaleProvider = ({
     children
 }: IProps) => {
    const handleChangeLocale = (newLocale: string) => {
        dayjs.locale(newLocale);
    };

    return <LocaleProvider
        localeDictionaries={serverDictionaries}
        defaultLocale={DEFAULT_LOCALE}
        onChangeLocale={handleChangeLocale}
        renderLoading={() => <Loader/>}
        persistKey={`${persistKey}-locale`}
        ignoreMissingLocaleMessage={false}
    >
        {children}
    </LocaleProvider>;
};

export default ReactLocaleProvider;
```


then in your src/app.tsx

```tsx

const App = () => {
    return (
        <Provider store={setup.store}>
            <ReactLocaleProvider>
                <AppRoute/>
            </ReactLocaleProvider>
        </Provider>
    );
};
```


### If you need async load dictionaries

in src/config/locale.ts

`${languageCode}-${countryCode}`

languageCode is a two-letter lowercase code as defined by ISO-639.
https://zh.wikipedia.org/zh-tw/ISO_639-1

countryCode is a two-letter uppercase code as defined by ISO-3166.
https://zh.wikipedia.org/zh-tw/ISO_3166-1

For example, en-US is the language value for United States English.

```tsx
import {TLocaleDictionariesAsync} from '@fengcaic/react-locale';

export enum ELocales {
    enUS = 'en-US',
    zhTW = 'zh-TW',
    jaJP = 'ja-JP',
}

export const localeDictionaries: TLocaleDictionariesAsync = {
    [ELocales.enUS]: () => import('@/locales/en-US').then((module) => module.default),
    [ELocales.zhTW]: () => import('@/locales/zh-TW').then((module) => module.default),
    [ELocales.jaJP]: () => import('@/locales/ja-JP').then((module) => module.default),
};
```

in src/library/react-locale/index.tsx

```tsx
import {LocaleAsyncProvider} from '@fengcaic/react-locale';
// ...ignore same

const ReactLocaleProvider = ({
     children
 }: IProps) => {
    const handleChangeLocale = (newLocale: string) => {
        dayjs.locale(newLocale);
    };

    return <LocaleAsyncProvider
        //...ignore same
    >
        {children}
    </LocaleAsyncProvider>;
};

export default ReactLocaleProvider;
```


### How to use

function component hook

```tsx
import {useLocale} from '@fengcaic/react-locale';


const Example = () => {
    const {t} = useLocale();
    return <div>{t('page.promotion.title', {def: 'promotions', params: {country: 'Taiwan'}})}</div>
}
```

global function (in not function component) `only in provider children component`

```ts
import {translateI18n} from '@fengcaic/react-locale';
translateI18n('page.promotion.title', {def: 'promotions', params: {country: 'taiwan'}})
```


## Option

if you use redux(global state) link locale, your can create custom Provider in your project

```tsx
import React, {Children} from 'react';
import {OriginLocaleProvider} from '@fengcaic/react-locale';
import {useDispatch, useSelector} from 'react-redux';
import {localeDictionaries, DEFAULT_LOCALE, ELocales} from './config/locale';

// Stores
import {selector, actions} from './store/main/language';

interface IProps {
    children: JSX.Element
}

const ReactLocaleProvider = ({
    children
}: IProps) => {
    const dispatch = useDispatch();
    const locale = useSelector(selector.selectLanguage);

    const handleChangeLocale = (locale: ELocales) => {
        dispatch(actions.setLocale({locale}));
    };

    return <OriginLocaleProvider
        localeDictionaries={localeDictionaries}
        defaultLocale={DEFAULT_LOCALE}
        locale={locale}
        setLocale={(locale: string ) => handleChangeLocale(locale as ELocales)}
        ignoreMissingLocaleMessage={false}
    >
        {Children.only(children)}
    </OriginLocaleProvider>;
};

export default ReactLocaleProvider;
```

## Storybook use (Not async load locale dictionaries)

```tsx
import {PartialStoryFn, Renderer} from '@storybook/types';
import {DEFAULT_LOCALE, ELocales} from "@/config/locale";
import {persistKey} from "@/config/app";
import LocaleProvider, {TLocaleDictionaries} from "@fengcaic/react-locale";
import localeEnUS from "@/locales/en-US";


const localeDictionaries: TLocaleDictionaries = {
    [ELocales.enUS]: localeEnUS,
};
const withI18next = (StoryFn: PartialStoryFn<Renderer>) => {

    return (
        <LocaleProvider
            localeDictionaries={localeDictionaries}
            defaultLocale={DEFAULT_LOCALE}
            persistKey={`${persistKey}-locale`}
            ignoreMissingLocaleMessage={false}
        >
            <StoryFn/>
        </LocaleProvider>
    );
};



// storybook preview.tsx
const preview: Preview = {
    parameters: {
        controls: {
            matchers: {
                color: /(background|color)$/i,
                date: /Date$/i,
            },
        },
    },
    decorators: [
        withI18next,
        (Story) => (
            <GridThemeProvider>
                <Story/>
            </GridThemeProvider>
        ),
    ],
};
```