YeonjuOHYE / javascript

0 stars 0 forks source link

react-intl #22

Open YeonjuOHYE opened 4 years ago

YeonjuOHYE commented 4 years ago

예시

https://github.com/formatjs/react-intl/tree/master/examples

const messages = {
  simple: 'Hello world',
  placeholder: 'Hello {name}',
  date: 'Hello {ts, date}',
  time: 'Hello {ts, time}',
  number: 'Hello {num, number}',
  plural: 'I have {num, plural, one {# dog} other {# dogs}}',
  select: 'I am a {gender, select, male {boy} female {girl}}',
  selectordinal: `I am the {order, selectordinal, 
        one {#st person} 
        two {#nd person}
        =3 {#rd person} 
        other {#th person}
    }`,
  richtext: 'I have <bold>{num, plural, one {# dog} other {# dogs}}</bold>',
  richertext:
    'I have & < &nbsp; <bold>{num, plural, one {# & dog} other {# dogs}}</bold>',
  unicode: 'Hello\u0020{placeholder}',
};

const App: React.FC<Props> = () => {
  return (
    <IntlProvider locale="en" messages={messages}>
      <p>
        <FormattedMessage id="simple" />
        <br />
        <FormattedMessage id="placeholder" values={{name: 'John'}} />
        <br />
        <FormattedMessage id="date" values={{ts: Date.now()}} />
        <br />
        <FormattedMessage id="time" values={{ts: Date.now()}} />
        <br />
        <FormattedMessage id="number" values={{num: Math.random() * 1000}} />
        <br />
        <FormattedMessage id="plural" values={{num: 1}} />
        <br />
        <FormattedMessage id="plural" values={{num: 99}} />
        <br />
        <FormattedMessage id="select" values={{gender: 'male'}} />
        <br />
        <FormattedMessage id="select" values={{gender: 'female'}} />
        <br />
        <FormattedMessage id="selectordinal" values={{order: 1}} />
        <br />
        <FormattedMessage id="selectordinal" values={{order: 2}} />
        <br />
        <FormattedMessage id="selectordinal" values={{order: 3}} />
        <br />
        <FormattedMessage id="selectordinal" values={{order: 4}} />
        <br />
        <FormattedMessage
          id="richtext"
          values={{num: 99, bold: (...chunks) => <strong>{chunks}</strong>}}
        />
        <br />
        <FormattedMessage
          id="richertext"
          values={{num: 99, bold: (...chunks) => <strong>{chunks}</strong>}}
        />
        <br />
        <FormattedMessage
          id="random"
          defaultMessage="I have < &nbsp; <bold>{num, plural, one {# dog} other {# & dogs}}</bold>"
          values={{num: 99, bold: (...chunks) => <strong>{chunks}</strong>}}
        />
        <br />
        <FormattedMessage id="unicode" values={{placeholder: 'world'}} />
        <br />
        <FormattedMessage
          id="whatever"
          defaultMessage="Hello\u0020{placeholder}"
          values={{placeholder: 'world'}}
        />
      </p>
    </IntlProvider>
  );
};

Introduction

Creating an I18n Context

ReactDOM.render(
  <IntlProvider locale={usersLocale} messages={translationsForUsersLocale}>
    <App />
  </IntlProvider>,
  document.getElementById('container')
);

Formatting Data

import React from 'react';
import ReactDOM from 'react-dom';
import {injectIntl, IntlProvider, FormattedRelative} from 'react-intl';

const PostDate = injectIntl(({date, intl}) => (
  <span title={intl.formatDate(date)}>
    <FormattedRelative value={date} />
  </span>
));

const App = ({post}) => (
  <div>
    <h1>{post.title}</h1>
    <p>
      <PostDate date={post.date} />
    </p>
    <div>{post.body}</div>
  </div>
);

ReactDOM.render(
  <IntlProvider locale={navigator.language}>
    <App
      post={{
        title: 'Hello, World!',
        date: new Date(1459913574887),
        body: 'Amazing content.',
      }}
    />
  </IntlProvider>,
  document.getElementById('container')
);

Core Concepts

▶︎Formatters (Date, Number, Message, Relative)

<FormattedDate
  value={new Date(1459832991883)}
  year="numeric"
  month="long"
  day="2-digit"
/>

April 05, 2016
<FormattedDateParts
  value={new Date(1459832991883)}
  year="numeric"
  month="long"
  day="2-digit"
>
  {parts => (
    <>
      <b>{parts[0].value}</b>
      {parts[1].value}
      <small>{parts[2].value}</small>
    </>
  )}
</FormattedDateParts>

<b>April</b> <small>05</small>

▶︎Provider and Injector ▶︎API and Components ▶︎Message Descriptor 번역에 필요한 정보를 넘겨주는 객체

type MessageDescriptor = {
  id: string;
  defaultMessage?: string;
  description?: string | object;
};

message extraction

import {defineMessages} from 'react-intl';

defineMessages({
  foo: {
    id: 'foo',
    defaultMessage: 'foo',
    description: 'bar',
  },
});
import {FormattedMessage} from 'react-intl';

<FormattedMessage id="foo" defaultMessage="foo" description="bar" />;
function Comp(props) {
  const {intl} = props;
  return intl.formatMessage({
    // The whole `intl.formatMessage` is required so we can extract
    id: 'foo',
    defaultMessage: 'foo',
    description: 'bar',
  });
}

▶︎Message Syntax ICU Message Syntax 기반으로 복잡한 syntax도 제공가능

Hello, {name}, you have {itemCount, plural,
    =0 {no items}
    one {# item}
    other {# items}
}.

▶︎Defining default messages for extraction ▶︎Custom, named formats

Component

Why Components? (API 직접 쓰는 것 보다 좋은 이유)

index

API

API 직접 쓰는 것 보다 <Formatted*> 사용하는 것이 더 많은 이점이 있다. API는 title, aria attribute 같이 ReactElement가 적합하지 않은 곳에 쓴다.

 <span title={intl.formatDate(date)}>
    <FormattedRelative value={date} />
  </span>

index

example

import * as React from 'react';
import {IntlProvider, useIntl, injectIntl, IntlShape} from '../';

const Comp: React.FC<{}> = _ => {
  const {formatDate} = useIntl();
  return <h1>{formatDate(Date.now())}</h1>;
};

const Comp2: React.FC<{intl: IntlShape}> = ({
  intl: {formatDate, formatTime},
}) => {
  return (
    <>
      <h1>{formatDate(new Date(), {month: 'long'})}</h1>
      <h2>{formatTime(undefined)}</h2>
    </>
  );
};

const Comp2WithIntl = injectIntl(Comp2);

interface Props {
  currentTime?: Date | number;
}

const App: React.FC<Props> = _ => {
  return (
    <IntlProvider locale="en" timeZone="Asia/Tokyo">
      <div>
        <Comp />
        <Comp2WithIntl />
      </div>
    </IntlProvider>
  );
};

App.defaultProps = {
  currentTime: new Date(),
};

export default App;

translations in files

js 파일

const messages = {
  simple: 'Hello world',
  placeholder: 'Hello {name}',
  date: 'Hello {ts, date}',
  time: 'Hello {ts, time}',
  number: 'Hello {num, number}',
  plural: 'I have {num, plural, one {# dog} other {# dogs}}',
  select: 'I am a {gender, select, male {boy} female {girl}}',
  selectordinal: `I am the {order, selectordinal, 
        one {#st person} 
        two {#nd person}
        =3 {#rd person} 
        other {#th person}
    }`,
  richtext: 'I have <bold>{num, plural, one {# dog} other {# dogs}}</bold>',
  richertext:
    'I have & < &nbsp; <bold>{num, plural, one {# & dog} other {# dogs}}</bold>',
  unicode: 'Hello\u0020{placeholder}',
};

const App: React.FC<Props> = () => {
  return (
    <IntlProvider locale="en" messages={messages}>
      <p>

json

import messages_de from "./translations/de.json";
import messages_en from "./translations/en.json";

const messages = {
    'de': messages_de,
    'en': messages_en
};
const language = navigator.language.split(/[-_]/)[0];  // language without region code

ReactDOM.render(
    <IntlProvider locale={language} messages={messages[language]}>
    ...
YeonjuOHYE commented 4 years ago

html Tag 처리

react-intl

   <FormattedHTMLMessage
      id="welcome"
      defaultMessage={`<strong>Hello</strong> {name}, you have {count, number} {count, plural,
                      one {message}
                      other {messages}
                    }`}
      values={{ name: name, count }}
    />
class FormattedHTMLMessage extends FormattedMessage<
  Record<string, PrimitiveType>
> {
  static displayName = 'FormattedHTMLMessage';
  static defaultProps = {
    ...FormattedMessage.defaultProps,
    tagName: 'span' as 'span',
  };
  render(): JSX.Element {
    return (
      <Context.Consumer>
        {(intl): React.ReactNode => {
          if (!this.props.defaultMessage) {
            invariantIntlContext(intl);
          }

          const {formatHTMLMessage, textComponent} = intl;
          const {
            id,
            description,
            defaultMessage,
            values: rawValues,
            children,
          } = this.props;

          let {tagName: Component} = this.props;

          // This is bc of TS3.3 doesn't recognize `defaultProps`
          if (!Component) {
            Component = textComponent || 'span';
          }

          const descriptor = {id, description, defaultMessage};
          const formattedHTMLMessage = formatHTMLMessage(descriptor, rawValues);

          if (typeof children === 'function') {
            return children(formattedHTMLMessage);
          }

          // Since the message presumably has HTML in it, we need to set
          // `innerHTML` in order for it to be rendered and not escaped by React.
          // To be safe, all string prop values were escaped when formatting the
          // message. It is assumed that the message is not UGC, and came from the
          // developer making it more like a template.
          //
          // Note: There's a perf impact of using this component since there's no
          // way for React to do its virtual DOM diffing.
          const html = {__html: formattedHTMLMessage};
          return <Component dangerouslySetInnerHTML={html} />;
        }}
      </Context.Consumer>
    );
  }
}

react-i18next

<div dangerouslySetInnerHTML={{ __html: t('my-label', { link: yourURL }) }} />