あいつの日誌β

あいつの日誌です。

i18next を使って国際化してみる

github.com

あらすじ

とあるサイトの開発に携わっているのですが、将来的に国際化して海外ユーザーもターゲットとしたい。という話なので予め国際化する準備をしておきたい。

最初は react-intl を試そうとしましたが、どうやら react-helmet との相性があんまり良くなさそう。使えるんだけどインターフェースが複雑。

そんな訳で次に react-i18next を試そうと思ったのですが、そもそも react-i18next の使い方を覚えたほうがいいなと思ったので練習しました。

やりたい事

  • 日本語、英語に切り分けたい
  • `{t(‘key’)} と書いたらいい感じになる
  • {t('key', 引数)} のように引数を渡したい
  • t('some.key)}` のよう書きたい

将来的に国際化したいのですがいきなりそこまで頑張りたくもないです。なんですが Form Validation のところだけは最初から国際化したいのでそこだけ頑張りたいです。

どうせサーバーサイドとのやりとりでエラー文言は英語になるだろうし…

just do it

create-react-app practice-i18next && cd $_
mkdir -p locales/{en,ja}
touch locales/{en,ja}/translation.json
yarn add i18next --save

create locales/en/translation.json

{
  "hello": "hello",
  "world": "world",
  "welcome": "welcome, {{name}}!!",
  "validation": {
    "isNotEmail": "{{email}} is not valid email."
  }
}

create locales/ja/translation.json

{
  "hello": "こんにちは",
  "world": "世界",
  "welcome": "ようこそ, {{name}}さん!!",
  "validation": {
    "isNotEmail": "{{email}} は正しい形式ではありません"
  }
}

edit src/App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import i18next from 'i18next';
import enLocalesTranslationJson from './locales/en/translation'
import jaLocalesTranslationJson from './locales/ja/translation'

i18next
  .init({
    fallbackLng: 'ja',
    debug: true,
    resources: {
      en: {
        translation: enLocalesTranslationJson
      },
      ja: {
        translation: jaLocalesTranslationJson
      }
    }
  });

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
         <h2>{i18next.t('hello')}</h2>
         <h2>{i18next.t('world')}</h2>
         <h2>{i18next.t('welcome', {name: "田中"})}</h2>
         <h2 style={{color: "red"}}>{i18next.t('validation.isNotEmail', {email: "tanaka@"})}</h2>
      </div>
    );
  }
}

export default App;

動作確認

yarn start

f:id:okamuuu:20170922194126p:plain

まとめ

react-i18next という公式な react 向けの wrapper があるようですが、なんだか i18next で事足りる気がする。react-i18next を使ったほうがいいのかは後々考える事にします。

Gatsby.js の Tutorial で Error: Unable to find plugin "gatsby-plugin-typography" と言われる場合

結論

プロジェクトのディレクトリに移動して npm install --save gatsby すると良いでしょう。issue あげたら npm の問題であるとの事ですが、gatsby 側からは特に対応しないようです。とはいえ tutorial 試して見て動かないから使うのやめた、という人達が出てきてしまう気がするのでちょっと気になる。まあ Gatsby.js が結構バギーな状態の気がするので初学者が何かするにはそもそも敷居が高い気がするのでしょうがないのかな。*1

Error: Unable to find plugin "gatsby-plugin-typography" in Gatsby.js Tutorial Part Two · Issue #1989 · gatsbyjs/gatsby · GitHub

問題

I tried Gatsby.js Tutorial Part Two but I found the error message.

環境

% node -v
v8.1.0

% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.6
BuildVersion:   16G29

再現方法

I typed the following commands.

% cd 
% npm install --global gatsby-cli
% gatsby --version
1.1.6
% gatsby new tutorial-part-two https://github.com/gatsbyjs/gatsby-starter-hello-world
% cd tutorial-part-two

At this point, I can run the gatsby develop. But I found the error message when I create the gatsby-config.js.

% cat << EOT > gatsby-config.js
module.exports = {
  plugins: ["gatsby-plugin-typography"],
}
EOT
% npm install --save gatsby-plugin-typography 

the error

% npm run develop

> gatsby-starter-hello-world@ develop /Users/okamuuu/tutorial-part-two
> gatsby develop

success open and validate gatsby-config.js — 0.005 s
(node:76343) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Unable to find plugin "gatsby-plugin-typography"
(node:76343) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

解決策?

Looks like node_modules/gatsby is almost empty.

% ls -al node_modules/gatsby
total 0
drwxr-xr-x   3 okamuuu  staff   102  9  1 23:51 .
drwxr-xr-x  94 okamuuu  staff  3196  9  1 23:51 ..
drwxr-xr-x   3 okamuuu  staff   102  9  1 23:51 node_modules

So I tried to type npm install --save gatsby. It looks good to me.

%  ls node_modules/gatsby/
README.md       cache-dir       node_modules    yarn.lock
bin             dist            package.json

*1:個人的には結構気に入っていて最近ずっと触っています

React で Latex を含む Markdown を扱いたいので markdown-it-katex を使った

f:id:okamuuu:20170829141649p:plain

あらすじ

React で Markdown を使いたい場合は react-markdown が存在します。React で latex を使いたい場合は react-katex が存在します。なんだけど MarkdownLatex を含む場合はどうやればいいのか知らないので調べました。

結論

markdown-it とその plugin である markdown-it-katex を使う

Just Do it

準備

create-react-app practice-react-markdown-it-katex && cd $_
getstorybook
yarn add markdown-it markdown-it-katex @storybook/addon-knobs gh-pages

edit stories/index.js

 
 import { Button, Welcome } from '@storybook/react/demo';
 
+import 'katex/dist/katex.min.css'
+import MarkdownIt from 'markdown-it'
+import mk from 'markdown-it-katex'
+
+const md = new MarkdownIt()
+md.use(mk)
+
 storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
 
 storiesOf('Button', module)
   .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
   .add('with some emoji', () => <Button onClick={action('clicked')}><U+1F600> <U+1F60E> <U+1F44D> <U+1F4AF></Button>);
+
+const result = md.render('# Math Rulez! \n  $\\sqrt{3x-1}+(1+x)^2$');
+
+storiesOf('markdown-it', module)
+  .add('katex', () =>
+    <div dangerouslySetInnerHTML={{ __html: result }} />
+  )

yarn run storybookhttp://localhost:6006 で動作確認できます(port は各々の環境によって異なる場合があります)

注意点は下記の2点

  • markdown-it が parse した HTML はJSX(仮想DOM)ではないので React で使う場合は dangerouslySetInnerHTML を使用します
  • katex が使用する CSSmarkdown-it-katex を install したときに node_modules/katex/dist/katex.min.css に入っているはずなのでそれを使用します。

storybook で動作確認

とあるプロジェクトで数学の問題を Markdown 形式で記述しているチームがいるのでこの compnent が要件を満たしているかどうかをチェックしてもらいます。

今回は gh-pages に簡単に上げられるので storybook とその addons を使う事にします

edit .storybook/addons.js

 import '@storybook/addon-actions/register';
 import '@storybook/addon-links/register';
+import '@storybook/addon-knobs/register';

edit stories/index.js

 import { storiesOf } from '@storybook/react';
 import { action } from '@storybook/addon-actions';
 import { linkTo } from '@storybook/addon-links';
+import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
+
+const stories = storiesOf('Storybook Knobs', module);
+stories.addDecorator(withKnobs)
 
 import { Button, Welcome } from '@storybook/react/demo';
 
@@ -26,3 +30,11 @@ storiesOf('markdown-it', module)
     <div dangerouslySetInnerHTML={{ __html: result }} />
   )
 
+const markdown = '# Math Rulez! \n  $\\sqrt{3x-1}+(1+x)^2$'
+
+stories.add('markdown-it-katex with knobs', () => {
+  const parsedMarkdown = md.render(text('Markdown', markdown))
+  return (
+    <div dangerouslySetInnerHTML={{ __html: parsedMarkdown }} />
+  )
+})

この状態で yarn run storybook を再起動します。右下に KNOBS のタグが追加されているのでここに Latex を含んだ Markdown を入力してもらい、期待動作として正しいのか確認してもらいます。

f:id:okamuuu:20170829141548p:plain

gh-pages

まず github にある自身の repository を用意しておきます。

yarn run build-storybook を実行して、 storybook-static 配下に storybook を静的ファイルとして生成します。その後 $(npm bin)/gh-pages -d storybook-static を実行します。

これでコンテンツ作成チームに記述した Markdown がきちんと動いているかを確認してもらえるようになりました。

まとめ

というわけで無事 React で作られて Component に Latex を導入することができました。

上記の例だと Latex の記述が先頭と最後尾に $ が入ってくる想定になっていますがそこは各自で適宜調整頂ければと思います。

こちらからは以上です。

github.com

第69回青梅市納涼花火大会に行って来た

f:id:okamuuu:20170808183458p:plain

あらすじ

とある友人が「夏の思い出作りたいわー」と言い出したのでみんなで花火を見に行くことになりました。そして引率するのが私になったのですが、都内の花火大会は人が多いから引率するの大変そうなのでどうしたものかと思ったら地方で花火を見るという選択肢を思いつきました

8月5日(土)に開催されている花火大会を探す

さて、日程はすでに決まっているのですが、まだどの花火大会に行くのか決まっていません。条件は以下で設定しました。

  • 8月5日に開催される
  • 東京近郊
  • 人あんまりいない

で、第69回青梅市納涼花火大会を発見したのでこれにしました。決め手になってのは打ち上げ数に対して人出が少ないというところです。

名称 打ち上げ数 前年の人出
第69回青梅市納涼花火大会 約3600発 1万4000人
第58回いたばし花火大会 約1万2000発 52万人
第42回江戸川区花火大会 約1万4000発 90万人

花火の数はメジャーな大会に比べて1/3ですがそれでも 19:30-20:40 まで打ち上げるので十分楽しめる本数だと思います。

感想

結果、この花火大会はなかなかの穴場スポットでした。人数が少ないので場所取りも穏やかで、花火までの距離が近くて迫力満点でした。

到着時間が少し遅れたこともあって*1無料開放されている青梅私立第一小学校に入れなかったのですが、その周りに腰掛けてみんなで仲良く、ビール片手に花火を鑑賞してきました。

まとめ

都内の花火大会はなんだかんだで花火との距離が遠いし、人がいっぱいで疲れるから、花火は地方で見るのが正解なんじゃないかな。

こちらからは以上です。

*1:浴衣を着てくる女子がいる場合は大体そうなるでしょう

MacOSX: ターミナルで複数ファイルの文字列を一括置換する

もう何度も同じ事を繰り返すのでMEMO

MacOSX に入っている sed コマンドは Linux のとは少し違うので brew install

brew install gnu-sed --default-names

でこんな感じで実行

find ./src -name '*.html' | xargs sed -i 's/xxx/yyy/g'

Gatsby v1.3.0 を試す(3)

前回のおさらい

Gatsby は Node.js 製の Static Site Generator です。JSON ファイルのようなデータ構造からも静的ファイルを生成する事ができます。 今回は WebAPI から fetch する方法を紹介します。

前々回、前回と違い外部API から resouce を取得して Gatsby の node を生成する plugin は v1.5.3 時点でまだ存在しません。その為自身でこれらの処理を記述する必要があります。

準備

プロジェクトを作成します。

gatsby new practice-gatsby-http && cd $_
yarn add node-fetch
touch gatsby-node.js

今回使用する WebAPI ですが、面倒なので http://jsonplaceholder.typicode.com/posts をそのまま使用します。

gatsby-node.js に sourceNodes 関数を追加します。sourceNodes に関しての詳しい説明は以下のページをご覧ください。

https://www.gatsbyjs.org/docs/node-apis/#sourceNodes

sourceNodes 関数

nodes 生成時に必要なデータをファイルから読み込む為に gatsby-node.js に以下の処理を追記します。

yarn add node-fetch

gatsby-node.js を修正します。

const crypto = require('crypto');
const _ = require(`lodash`);

exports.sourceNodes = async ({ boundActionCreators }) => {
  const { createNode } = boundActionCreators

  const posts = await fetch("http://jsonplaceholder.typicode.com/posts?_limit=20").then(res => res.json())

  // Process data into nodes.
  posts.forEach((post) => {
    createNode({
      post,
      id: post.id + "", 
      children: [], 
      parent: "posts",
      internal: {
        type: "posts",
        content: JSON.stringify(post),
        contentDigest: crypto
          .createHash(`md5`)
          .update(JSON.stringify(post))
          .digest(`hex`),
      }   
    })  
  })  
  return
}

動作確認

GraphiQL で allPosts と posts クエリーをそれぞれ発行する

query {
  allPosts {
    edges {
      node {
        id
        post {
          id
          title
          body
        }
      }
    }
  }
}
query {
  posts(id: {eq: "1"}) {
    post {
      id
      title
      body
    }
  }
}

まとめ

というわけで3回に渡って Gatsby の主幹となる nodes を生成する処理について紹介しました。実際に Page を生成するにはも少しステップが必要なのですがまた気が向いた時に tutorial でも作りたいと思います。

Gatsby v1.3.0 を試す(2)

前回のおさらい

React.js の component を使って Blog などを生成できる Gatsby という Node.js 製の Statice site generator を紹介しました。 Markdown だけでなく JSON ファイルのようなデータ構造からも静的ファイルを生成する事ができるので今回はそのやり方を紹介します。

ちなみに、v1.3 を試していたらすでに v1.5 になっていました。タイトルどうしよう。

準備

プロジェクトを作成する

gatsby new practice-gatsby-json && cd $_

Source plugin を追加する

nodes 生成時に必要なデータをファイルから読み込む為のプラグインを追加する。

yarn add gatsby-source-filesystem

gatsby-config.js を修正し、プラグインを設定します。

module.exports = {
   siteMetadata: {
     title: `Gatsby Default Starter`,
   },
-  plugins: [`gatsby-plugin-react-helmet`],
+  plugins: [
+    `gatsby-plugin-react-helmet`,
+     {
+      resolve: `gatsby-source-filesystem`,
+      options: {
+        path: `${__dirname}/src/pages`,
+        name: 'pages',
+      },
+    }
+  ],
 }

Transformer plugins を追加する

nodes 生成時に JSON 形式から変換させるプラグインを追加する

yarn add gatsby-transformer-json

gatsby-config.js を修正し、プラグインを設定します。

         path: `${__dirname}/src/pages`,
         name: 'pages',
       },
-    }
+    },
+    `gatsby-transformer-json`,
   ],
 }

json ファイルを用意

jsonplaceholder のデータをお借りします。以下のような json ファイルを src/pages/posts.json として保存します。

[
  {
    "id": "1",
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },  
  {
    "id": "2",
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  }
]

動作確認

yarn develop

http://localhost:8000/___graphql を開いて以下のクエリを実行します。

一覧取得

{
  allPostsJson {
    edges {
      node {
        id
        title
        body
      }
    }
  }
}

f:id:okamuuu:20170729145544p:plain

詳細取得

query {
  postsJson(id: {eq: "1"}) {
    id
    title
    body
  }
}

f:id:okamuuu:20170729145556p:plain

まとめ

というわけで JSON ファイルから nodes を生成する方法を紹介しました。gatsby@1.0.0 以降から nodes を生成するのに markdown 以外の方法が増えたので gatsby の可能性に私はとても期待しています。

次回は http で外部API を fetch して、そこから nodes を生成する方法を紹介します。