2020年5月23日

Macの音声読み上げ機能を言語別にキー一発で呼び出す方法

読み上げ機能は目に優しい


最近、ブラウザでニュースを読む時にMacの音声読み上げ機能を使うと、「目を閉じていてもニュースが頭に入ってくる」ことに気付きました。


これは慣れるとかなり便利。なんと言っても目の疲れが大幅に軽減できることが最大の利点です。







もちろんMacの標準状態でも、ブラウザで文章を選択して「Edit」メニュー → 「Start Speaking」を選べば読み上げ機能を使うことは可能です。

アクセシビリティの設定で任意のショートカットキーを設定することも出来ます。



もし一つの言語しか読み上げさせる必要がないのであれば、これで大丈夫です。


ただ、複数の言語を使い分けたい場合は、これだと読み上げに使用する言語(音声)をその都度選ぶことが出来ません。

日本語の音声を設定している状態で英語の文章を読み上げさせても、カナカナ読みの変な発音でしか読まれません。逆に英語の音声が選択されている状態で日本語を読み上げさせようとしても、上手く発音してくれません。

つまり、選択されている文章の言語によって読み上げに使う音声を切り替える必要があるわけです。


これが出来るように今回設定した方法を、以下にメモしておきます。


1. 言語別にAutomatorでサービスを作成


Automatorを起動して、新しい Quick Action を作成します。




Actionsから System → Speak Text を選んで、ドラッグアンドドロップで追加します。




読み上げに使う言語に対応した音声を選んで、後から分かる名前を付けて保存します。


これを、使いたい言語の数だけ繰り返します。



2. キーボードショートカットを設定


システム設定から「Keyboard」を選ぶと、「Services」のカテゴリ内に先ほどAutomatorで作ったQuick Actionが表示されます。




それぞれの言語に対応したQuick ActionのチェックをONにして、好きなショートカットキーを設定すれば完了です。

私の場合は、

Ctrl + Cmd + w  → 日本語読み上げ
Ctrl + Cmd + e  → 英語読み上げ

としています。

なるべく片手ですぐ押さえられる組み合わせが良いと思います。


ブラウザを開いて何らかの文章を選択状態にすると、Servicesメニューに上で作成したQuick Actionが追加されているのが分かります。






この設定で、Google Chrome、Safari, Firefox のいずれのブラウザでも文章を選択してキー一発で言語別に読み上げ機能を使うことが出来るようになりました!







 

2020年5月7日

Firebase Hostingにデプロイした静的サイトにFirebase Analyticsを追加する(Next.js)

まずは、Firebaseの管理画面から、該当のプロジェクトでAnalyticsを有効化します。


Google アナリティクスを使ってみる






ここではNext.jsからエクスポートした静的サイトを例にします。

pages/index.js など、全てのページで共通に含まれる部分で、下のようにHeadコンポーネントを使ってscriptタグを埋め込みます。

要は下の赤字部分の3行のscriptタグがHTMLのhead部分に含まれるようにすれば良いということですね。

export default function Home({ allPostsData }) {
  return (
    <Layout home>
      <Head>
        <title>{siteTitle}</title>
        <script src="/__/firebase/7.14.2/firebase-app.js"></script>
        <script src="/__/firebase/7.14.2/firebase-analytics.js"></script>
        <script src="/__/firebase/init.js"></script>
      </Head>
      <section className={utilStyles.headingMd}>
        <p>[Your Self Introduction]</p>
      </section>
      <section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
        <h2 className={utilStyles.headingLg}>Blog</h2>
        <ul className={utilStyles.list}>
          {allPostsData.map(({ id, date, title }) => (
            <li className={utilStyles.listItem} key={id}>
              <Link href="/posts/[id]" as={`/posts/${id}`}>
                <a>{title}</a>
              </Link>
            </li>
          ))}
        </ul>
      </section>
    </Layout>
  );
}


次に、下のドキュメントの通りにイベントを送信する必要があります。

イベントをロギングする

このために、「send-ga-event.js」 と 「use-send-screenview.js」という2つのファイルを作成しました。


send-ga-event.js

export function sendGAEvent(eventName, params) {
  const firebase = window && window.firebase;
  if (!firebase) return;
  try {
    firebase.analytics().logEvent(eventName, params);
  } catch (error) {
    console.error(error);
  }
}


use-send-screenview.js

import { useEffect } from 'react';
import { sendGAEvent } from './send-ga-event';
export function useSendScreenView(screenName) {
  return useEffect(() => {
    const params = { screen_name: screenName };
    sendGAEvent('screen_view', params);
  }, []);
}

上で定義したuseSendScreenView()という名前のフックを pages/index.jsから呼び出すことでAnalyticsにイベントを送信します。


pages/index.js

import { useSendScreenView } from '../utils/use-send-screenview';
export default function Home({ allPostsData }) {
  useSendScreenView('home');
  return (
    <Layout home>
      <Head>
        <title>{siteTitle}</title>
        {process.env.NODE_ENV === 'production' && (
          <>
            <script src="/__/firebase/7.14.2/firebase-app.js"></script>
            <script src="/__/firebase/7.14.2/firebase-analytics.js"></script>
            <script src="/__/firebase/init.js"></script>
          </>
        )}
      </Head>
      <section className={utilStyles.headingMd}>
        <p>[Your Self Introduction]</p>

(以下省略...)


これらの変更を行ったあと、

npm run build && npm run export
firebase deploy 

でデプロイしてサイトを更新します。

更新後のページを開くと、数秒でAnalyticsの画面にイベントが表示されるのが確認出来ます。








  

Next.jsで静的サイトを出力してFirebase Hostingでホスティングする

まずは公式のドキュメントにしたがってNext.jsのアプリケーションを作成しましょう。

Create a Next.js App | Learn Next.js 


package.jsonファイルのscriptsセクションに下記を追加しておきます。

  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "export": "next export"
  },


npm run build && npm run export を実行して outフォルダに結果が出力されるのを確認します。



次にFirebase CLIを初期化します。


firebase login

firebase init



カーソルキーで「Hosting」を選択してスペースキーを押し、Enterで確定です。

Firebaseの管理画面でまだ「プロジェクト」を作成していない場合は、プロジェクトIDと名称を入力して新規作成します。既に使われているIDを入れてしまうとエラーになるので、ユニークなIDを入力しましょう。

アップロード対象のフォルダを聞かれるので、デフォルトの「public」ではなく「out」を指定します。


firebase deploy を実行して、「Deploy complete!」と表示されれば完了です!


慣れれば3分もかからずに出来てしまいますね。


バックエンド処理にCloud Functionsを使ったりするような場合も、下のドキュメントの通り設定すれば決して難しくはありません。

本当に便利な世の中になったものです。^^)


あ、特にFirebase Hostingでなくても良いのであれば、もちろんNext.jsの開発元であるVercel(旧Zeit)社のサービスを使うのが一番簡単でオススメです。



こちらであれば、静的サイトでなくSSRありのサイトであっても、またバックエンドのAPIが必要な場合でも、何も考えずにデプロイしてしまえばあとはサービス側で上手くやってくれるみたいなので、こちらも素晴らしいと思います。







 

2020年5月4日

ReactでSVGファイルをコンポーネントとしてレンダリングする

egghead.ioのレッスンを観ていて良さそうなものがあったので、メモしておきます。


Add SVGs as React Components with Create React App 2.0
https://egghead.io/lessons/react-add-svgs-as-react-components-with-create-react-app-2-0

Create React App のドキュメントにも記述があります。
https://create-react-app.dev/docs/adding-images-fonts-and-files


Create React Appで作ったReactアプリケーションでは画像ファイルを下のようにインポートしてコンポーネント内で参照することが出来ます。

import logo from './logo.png';
function Header() {
  return <img alt="Logo" src="{logo}" />;
}

もちろん画像の形式がSVGであっても同様です。

import logo from './logo.svg';
function Header() {
  return <img alt="Logo" src="{logo}" />;
}

ただ、SVG形式の場合はインポートの仕方を下のように変えるとReactコンポーネントとしても使えるようになるそうです。

import { ReactComponent as Logo } from './logo.svg';
function App() {
  return (
    <div className="App">
<header className="App-header">
<Logo className="App-logo" />
</header>
</div>);
}


このようにすると、HTMLとしてレンダリングされる際のタグが imgタグではなく、svgタグになります。

svgタグになってうれしい点は、SVG内の個々の要素(図形)をCSSでコントロール出来るようになることです。


例えば、線の色や太さを変えたり、アニメーションを付け加えたりすることが可能になります。


下の例では、Reactロゴのpathエレメントの部分のみ点線にして、さらに点線のオフセットをアニメーションで動かしています。

.App-logo path {
  stroke: palegoldenrod;
  stroke-width: 10px;
  fill: none;
  stroke-dasharray: 35px 15px;
  animation: orbit 1s infinite linear;
} @keyframes orbit {
  to {
    stroke-dashoffset: 50px;
  }
}

実行するとこんな感じになります。