20201017のvue.jsに関する記事は7件です。

【Vue.js】リアクティブに画像プレビューする

new FileReaderについて

アップロード画像を非同期に読み取ることができるオブジェクト

const reader = new FileReader();

FileReaderでできること

読み込まれた画像情報をアップロード画像データに上書きする

reader.onload = (e) => {
  this.staff_image = e.target.result;
};

アップロード画像を読み込む

reader.readAsDataURL(this.staff_image);
  • 読み取り操作が正常に完了されたら、result属性に格納されたデータを、アップロード画像データに上書きする
  • アップロード画像を読み込ませた結果をresult属性に返している

実践:アップロード画像をプレビューで表示させて、DBに保存する

ポイント

  • プレビュー画像と、DB保存画像の格納dataを分けて考える

流れ

  1. v-input-fileに、v-model属性をつけることで、アップロードファイルとdataオプションと連動させる
  2. 画像がアップロードされたらimgaeUp関数が着火し、画像ファイル拡張子を使えるものに限定させる。この時、DBに保存する画像情報を格納しておく。
  3. もし使えない画像拡張の場合は、errors変数にエラー文を格納すると共に画像情報を削除し、imageUp関数を強制終了させる
  4. 特に問題ない場合、プレビュー画像用に画像情報をパースし、アップロード画像を表示させる判定に切り替える。

tmplateタグ(pug形式)

v-img(v-if="image", :src="staff_image")
v-img(v-else, src="/images/default.png")
v-file-input(
  hide-input,                   // フォームを隠す
  prepend-icon="mdi-camera",    // ボタンアイコン 
  @change="imageUp()",          // アップロードされたら、着火する関数
  v-model="staff_image"         // アップロード画像と連動させる
)

scriptタグ

data() {
  return {
    staff_image: "",     // アップロードファイルの格納場所
    image_name: "",      // DBに保存するアップロードファイル
    image: false,        // 表示するのは、デフォルト画像かアップロード画像かの判定
  };
},

imageUp() {

  // 画像ファイル拡張子の検証
  const type = this.staff_image.type;
  this.image_name = this.staff_image;    // DB保存用に、画像情報を格納しておく
  let errors = "";
  if (
    type !== "image/jpeg" &&
    type !== "image/gif" &&
    type !== "image/png" &&
    type !== "application/pdf"
  ) {
    errors +=
    ".jpg、.gif、.png、.pdfのいずれかのファイルのみ許可されています\n";
  }
  if (errors) {
    alert(errors);
    this.staff_image = "";
    this.image_name = "";
    this.image = false;
    return;                             // エラー処理の場合、imageUp関数を強制終了する
  }

  // アップロード画像をプレビューで表示させる
  const reader = new FileReader();
  reader.onload = (e) => {
    this.staff_image = e.target.result;
  };
  reader.readAsDataURL(this.staff_image);
  this.image = true;                         // アップロード画像の表示に切り替える
},

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.js + Composition API(provide/inject) + Firebase Authenticationでミニマルな認証管理基盤を作成する

普段私はNuxt.jsを使っているのですが、今までのVue2時には、(他の選択肢を知らずに)Vuexで認証情報を管理していました。
しかし、Vue3ではprovide/injectを使ったステート管理が中々推されており、業務の中でこの度チャレンジしました。
そして『Nuxt.js + Composition API(provide/inject) + Firebaseでの最低限の認証機能を実装した』という記事は現状まったく無かったため、この度自分で記事を書きます。

(※)この記事では、そもそもの『Firebase Authenticationの使い方』や『Nuxtの使い方』については言及しません。
(※)provide/injectのメリットについては、この方の記事が個人的に中々わかりやすかったです。
Vue.2までだと、『1つの.vueファイルの中にテンプレート/ステート/ロジックを全て書く必要があったのが、今後はそれらを別のファイルに書き分けられる』という事です。
https://qiita.com/karamage/items/4bc90f637487d3fcecf0


firebaseの情報はpluginで管理します。のちにimportします。

plugins/firebase.js
import * as firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
import 'firebase/functions'
import * as firebase_secret from '../_secret/firebase'  //ここにsecrets情報を入れている

firebase.initializeApp(firebase_secret)

export const auth = firebase.auth()
export const functions = firebase.app().functions('us-central1') //('asia-northeast1')
export const db = firebase.firestore()
export const timestamp = () => firebase.firestore.FieldValue.serverTimestamp()

また、殆どの方はFirebase Authenticationで認証を行った後、Cluoud Firestoreから等ユーザーの情報を引っ張ってきたいでしょう。
Cloud Firestoreでは、
/{user collection}/${userID}/フィールド(nameなど)というシンプルな構成をここでは想定します。

そしてNuxt.のルートディレクトリに/composablesディレクトリを作成し、その中に①store情報を使うための鍵のファイル(user-store-key.ts)②ストア本体のファイル(user-store.ts)を作ります。

composables/user-store-key.ts
import { InjectionKey } from '@vue/composition-api';
import { UserStoreType } from './user-store';

const UserStoreKey: InjectionKey<UserStoreType> = Symbol('UserStore');
export default UserStoreKey;
composables/user-store.ts
import { reactive, ref } from "@vue/composition-api"
import { auth, db, timestamp } from '../plugins/firebase'

export default function UserStore() {
  const state = reactive({
    id: "",
    email: "",
    name: "",
  })

  const GetUserStore =()=>  state;

  const SetUserStore = (user: firebase.User) => {
    console.log("Login Information", user)
    const ref_userinfo = db.collection('users').doc(user.uid)
    ref_userinfo.onSnapshot(doc => {
      if (doc.exists) {
        console.log("Obtained User contact_info from DB", doc.data()!.contact_info)
        const parsedUserInfo = {
          id: doc.id,
          email: user.email!,
          name: doc.data()!.name,
        }

        state = parsedUserInfo
        console.log("Reflected on state", GetUserStore())
      } else {
        console.log("No User Data")
      }            
    });
  }

  }

  return { SetUserStore, GetUserStore}
}

export type UserStoreType = ReturnType<typeof UserStore>

コレで最低限のストアの構築は完成です!
SetUserStore()が呼ばれた際、stateのidとemailはAuthenticationの情報をセットし、nameはFirestoreから引っ張ってきます。
また、ゲッターとしてGetUserStore()、セッターとしてSetUserStore()を定義しreturnする事で、stateに外部から直接アクセス出来ないようにする事で保守性を上げています。

次に、/layout/default.vueにて、SetUserStore()関数をonMounted()のタイミングで実行するようにします。
stateの内容は、ページをリロードされると消えてしまいますが、layoutsで定義してしまえば、ページをリロードされる旅にSetUserStore()関数が呼ばれ、Firebaseから値を引っ張ってきます。

layouts/default.vue
<template>
  <v-app >
    <v-main>
      <v-container class="pa-0">
        <nuxt />
      </v-container>
    </v-main>

  </v-app>
</template>

<script lang="ts">
import Vue from 'vue'
import { auth, db, timestamp } from '../plugins/firebase'
import { defineComponent, SetupContext, onMounted, provide, inject } from '@vue/composition-api'
import UserStore from "../composables/user-store"
import UserStoreKey from "../composables/user-store-key"
export default defineComponent({  
  setup(props, context) {
    provide(UserStoreKey, UserStore())   //ここでprovideを使用
    const store = inject(UserStoreKey) as UserStoreType  //ここでinjectを使用
    onMounted(() => {
      auth.onAuthStateChanged((user) => { 
        if (user) {
          store.SetUserStore(user)
        } else {
          console.log("Not Authenticated User")
        }
      })
    })
    return { store}
  }
})
</script>
<style scoped>
</style>

公式ドキュメントを読むと、provideは送信側/injectは受信側を想定しているようですが、上記の通り、provideとinjectを同時に使う事も問題ありません。

また、(Vue2の時は)この処理はmiddlewareに書いても動作しましたが、Vue3ではmiddlewareに書いても動作しません!!
何故なら、provide/inject関数はsetupコンテキストの中でしか動作しないためです。


終わりに

この度、初めてprovide/injectを使用しましたが、Vuexと比べると
 ①storeのファイルが肥大化しづらい
 ②Vuexと違いグローバル変数では無いため、そこで保持すべきでない情報を持たずに済む。
 ③(②にも関連するが) どこでstoreが変更がされているかを追いやすく、大規模開発でも破綻しづらい
 ④型推論がイイ!!
というメリットを感じました。今後積極的に使ってゆこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.js + Composition API(provide/inject) + Firebase Authenticationでミニマルな認証基盤を作成する

普段私はNuxt.jsを使っているのですが、今までのVue2時には、(他の選択肢を知らずに)Vuexで認証情報を管理していました。
しかし、Vue3ではprovide/injectを使ったステート管理が中々推されており、業務の中でこの度チャレンジしました。
そして『Nuxt.js + Composition API(provide/inject) + Firebaseでの最低限の認証機能を実装した』という記事は現状まったく無かったため、この度自分で記事を書きます。

(※)この記事では、そもそもの『Firebase Authenticationの使い方』や『Nuxtの使い方』については言及しません。
(※)provide/injectのメリットについては、この方の記事が個人的に中々わかりやすかったです。
Vue.2までだと、『1つの.vueファイルの中にテンプレート/ステート/ロジックを全て書く必要があったのが、今後はそれらを別のファイルに書き分けられる』という事です。
https://qiita.com/karamage/items/4bc90f637487d3fcecf0


firebaseの情報はpluginで管理します。のちにimportします。

plugins/firebase.js
import * as firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
import 'firebase/functions'
import * as firebase_secret from '../_secret/firebase'  //ここにsecrets情報を入れている

firebase.initializeApp(firebase_secret)

export const auth = firebase.auth()
export const functions = firebase.app().functions('us-central1') //('asia-northeast1')
export const db = firebase.firestore()
export const timestamp = () => firebase.firestore.FieldValue.serverTimestamp()

また、殆どの方はFirebase Authenticationで認証を行った後、Cluoud Firestoreから等ユーザーの情報を引っ張ってきたいでしょう。
Cloud Firestoreでは、
/{user collection}/${userID}/フィールド(nameなど)というシンプルな構成をここでは想定します。

そしてNuxt.のルートディレクトリに/composablesディレクトリを作成し、その中に①store情報を使うための鍵のファイル(user-store-key.ts)②ストア本体のファイル(user-store.ts)を作ります。

composables/user-store-key.ts
import { InjectionKey } from '@vue/composition-api';
import { UserStoreType } from './user-store';

const UserStoreKey: InjectionKey<UserStoreType> = Symbol('UserStore');
export default UserStoreKey;
composables/user-store.ts
import { reactive, ref } from "@vue/composition-api"
import { auth, db, timestamp } from '../plugins/firebase'

export default function UserStore() {
  const state = reactive({
    id: "",
    email: "",
    name: "",
  })

  const GetUserStore =()=>  state;

  const SetUserStore = (user: firebase.User) => {
    console.log("Login Information", user)
    const ref_userinfo = db.collection('users').doc(user.uid)
    ref_userinfo.onSnapshot(doc => {
      if (doc.exists) {
        console.log("Obtained User contact_info from DB", doc.data()!.contact_info)
        const parsedUserInfo = {
          id: doc.id,
          email: user.email!,
          name: doc.data()!.name,
        }

        state = parsedUserInfo
        console.log("Reflected on state", GetUserStore())
      } else {
        console.log("No User Data")
      }            
    });
  }

  }

  return { SetUserStore, GetUserStore}
}

export type UserStoreType = ReturnType<typeof UserStore>

コレで最低限のストアの構築は完成です!
SetUserStore()が呼ばれた際、stateのidとemailはAuthenticationの情報をセットし、nameはFirestoreから引っ張ってきます。
また、ゲッターとしてGetUserStore()、セッターとしてSetUserStore()を定義しreturnする事で、stateに外部から直接アクセス出来ないようにする事で保守性を上げています。

次に、/layout/default.vueにて、SetUserStore()関数をonMounted()のタイミングで実行するようにします。
stateの内容は、ページをリロードされると消えてしまいますが、layoutsで定義してしまえば、ページをリロードされる旅にSetUserStore()関数が呼ばれ、Firebaseから値を引っ張ってきます。

layouts/default.vue
<template>
  <v-app >
    <v-main>
      <v-container class="pa-0">
        <nuxt />
      </v-container>
    </v-main>

  </v-app>
</template>

<script lang="ts">
import Vue from 'vue'
import { auth, db, timestamp } from '../plugins/firebase'
import { defineComponent, SetupContext, onMounted, provide, inject } from '@vue/composition-api'
import UserStore from "../composables/user-store"
import UserStoreKey from "../composables/user-store-key"
export default defineComponent({  
  setup(props, context) {
    provide(UserStoreKey, UserStore())   //ここでprovideを使用
    const store = inject(UserStoreKey) as UserStoreType  //ここでinjectを使用
    onMounted(() => {
      auth.onAuthStateChanged((user) => { 
        if (user) {
          store.SetUserStore(user)
        } else {
          console.log("Not Authenticated User")
        }
      })
    })
    return { store}
  }
})
</script>
<style scoped>
</style>

公式ドキュメントを読むと、provideは送信側/injectは受信側を想定しているようですが、上記の通り、provideとinjectを同時に使う事も問題ありません。

また、(Vue2の時は)この処理はmiddlewareに書いても動作しましたが、Vue3ではmiddlewareに書いても動作しません!!
何故なら、provide/inject関数はsetupコンテキストの中でしか動作しないためです。


終わりに

この度、初めてprovide/injectを使用しましたが、Vuexと比べると
 ①storeのファイルが肥大化しづらい
 ②Vuexと違いグローバル変数では無いため、そこで保持すべきでない情報を持たずに済む。
 ③(②にも関連するが) どこでstoreが変更がされているかを追いやすく、大規模開発でも破綻しづらい
 ④型推論がイイ!!
というメリットを感じました。今後積極的に使ってゆこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】 transitionを使用してfade-in, fade-out

【ゴール】

【Vue.js】 transitionを使用してfade-in, fade-out

画面収録 2020-10-17 21.08.19.mov.gif

【環境】

mac catarina 10.156
Vue.js v2.6.12

【実装】


■ifで条件分岐
■transition name="fade"で、fadeモードに

Helloworld.vue
<template>
  <div>
    <h2>hi</h2>
    <button @click="show = !show">変更</button>
    <transition name="fade" >
      <p v-if="show">trasition</p>
    <transition>
  </div>
<template>

<script>

export default ({
  data(){
    return {
      show: true       
    }
  }
})
</script>

<style scoped>

.fade-enter-active,
.fade-leave-active {
  transion: opacity 0.5s;
}


.fade-enter,
.fade-leave-to{
  opacity: 0;
}

</style>

以上

【まとめ】

■transitionでアニメーションを作成
■nameでアニメーションの種類を指定してあげる
■style内でtransitionの設定をする

【オススメ記事】

■ 【Vue.js】クリック処理 @click
https://qiita.com/tanaka-yu3/items/e578cadf35a7bc024770

■ 【Vue.js】 IF文・For文 条件分岐、繰り返し処理
https://qiita.com/tanaka-yu3/items/0ccf9a11525331b764de

■ 【Vue.js】 vue-routerを利用してルーティング作成し画面遷移を!!
https://qiita.com/tanaka-yu3/items/1b1f5f2b2d9638f50d33

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

firebaseでpull request ごとにpreviewをdeployするgithub action

概要

firebaseで、spaをnetlify やvercel のようにプルリクごとにURLを変えてdeployしたい。

how to

firebaseの初期化

公式
firebase toolをグローバルにinstall
今回はdeployする directoryはdistをしていします。(firebaseのdefaultはpublic)

npm i -g firebase-tools
firebase login
firebase hosting #deloyするdirectoryをdist にする

channel deployコマンドをpackage.jsonに追加

previewようのdeployはchannel deployというコマンド使います。
そのため、以下のように scriptに追加します。


{
  "name": "firebase deploy app",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate",
    "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
    "lint": "yarn lint:js && yarn lint:style",
    "test": "jest",
    "channel-deploy": "firebase hosting:channel:deploy" ←# 追加
  },

github workflowを設定する

以下のように設定します。

firebase-deploy.yml
name: CI

on: [push]

jobs:
  FrontDeploy:
    name: FrontDeploy
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repo
      uses: actions/checkout@main
    - name: setup Node
      uses: actions/setup-node@v1
      with:
        node-version: '12'
        registry-url: 'https://registry.npmjs.org'
    - name: Install Dependencies
      run: yarn
    - name: Build
      run: yarn build
    - name: deploy to Firebase Hosting
      shell: bash
      run:  yarn channel-deploy $(echo ${GITHUB_REF#refs/heads/}) --token=${{ secrets.FIREBASE_TOKEN }}

こちらでは、branch名を取得しています。

$(echo ${GITHUB_REF#refs/heads/})

tokenを取得

以下コマンドで、tokenを取得できます

firebase login:ci

取得したtokenをFIREBASE_TOKEN にいれましょう。

Secrets.png

以上でdeployできるはずです!

twitterもフォローよろしくおねがいします。 !

https://twitter.com/yoshixj

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@vue/cliのインストールからVueプロジェクト作成まで

はじめに

今回は@vue/cliのインストールをしてから実際にVueプロジェクトを実行するまでを書いていきます!
Node.jsがインストールされている必要があるので、
まだインストールできていない人はNode.jsのインストール方法(Windows)を見てね!

目次

  1. @vue/cliのインストール
  2. vue create コマンドでVueプロジェクトの作成
  3. 実際にブラウザで確認してみよう!

1. @vue/cliのインストール

  • @vue/cliはVueプロジェクトの雛形を作るのを便利にしてくれるコマンドラインインタフェースです! 以下のコマンドを実行するとインストールできます!
$ npm install -g @vue/cli
  • @vue/cliが正しくインストールされているかを確認する場合は「vue --version」を実行し、 バージョン情報が表示されればOK!
$ vue --version
@vue/cli 4.5.7

2. vue create コマンドでVueプロジェクトの作成

  • 「vue create project-name」を実行すると作成されます(project-nameは自分の好きな名前で作れます)
$ vue create sample
  • 以下のように表示されたらエンターでOK!

1.png

  • Vueプロジェクトの雛形が以下のように生成されます

2.png

3. 実際にブラウザで確認してみよう!

  • 作成されたプロジェクトフォルダに移動し「npm run serve」を実行!
$ cd sample
$ npm run serve

3.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ボタン一つで画像から文字起こし&翻訳できるアプリを作った話

こんにちは、Yuiです。

この度、ボタン一つで画像から文字起こし&翻訳できるアプリを作りました!

機能としては下記のものをつけています。

  • 自動で利用者のブラウザの言語を読み取ってデフォルト言語に設定
  • ボタン一つで文字起こしされる
  • ボタン一つでデフォルト言語(利用者が普段使っている言語)に翻訳される
  • デフォルト言語以外に言語を変えたい場合、クリックで言語を変えることができる
  • 言語を変えた場合、全体的に多言語化される
  • 文字起こし&翻訳した後の文章はボタン一つで簡単にコピーできる

シンプルな作りで、誰が見てもすぐに使うことができるようなサービスを目指して作りました。

使った技術スタック

今回、APIの接続だけで完了するような簡単なアプリなので、DB設計は必要ありませんでした。
というわけで、使ったのは下記だけです。

  • Vue.js(フレームワークはVuetify利用)→フロントエンド
  • vue-clipboard2→クリックしたらコピーできるように利用
  • axios→APIとの通信のために利用
  • vue-i18n→多言語化のために利用
  • API→Cloud Vision APIとCloud Translation APIを利用

上記で大体かかった時間は合計30時間ぐらいでした。
デザイン→1時間
画面設計→5時間
API接続→10分
多言語化→2時間
残りはレイアウトの微調整とかでこだわった部分で時間がかかりました。笑

若干ハマったところ

今回少し詰まったところとしてはplaceholderだけv-bindさせて使う必要があるということと、自動で利用者のブラウザの言語を読み取るという部分があります。

まず、今回多言語化をしたので、i18nの書き方に従い、

<div>{{$t('message.welcome')}}</div>

のように書く必要があります。

ただ、placeholderの文字を多言語化するにあたって、どういう風にかけば良いか悩みました。

//ファイルアップロード部分
<v-file-input
  v-model="file"
  @change="setImage"
  @click:clear="clearData"
  accept="image/*"
  prepend-icon="mdi-camera"
  placeholder="ここにプレースホルダーに表示させる文字" //<=この部分をどう多言語化する?
></v-file-input>

その部分に関して書いてあるものが少なく少し悩んだのですが、公式ドキュメントを見るとどうやらv-bindさせて書けば良さそうだったので、以下のようにして書きました。

//ファイルアップロード部分
<v-file-input
  v-model="file"
  @change="setImage"
  @click:clear="clearData"
  accept="image/*"
  prepend-icon="mdi-camera"
  :placeholder="$t('message.welcome')"
></v-file-input>

これでOK。ちなみに今回すでに文字起こしや翻訳をした後に再度違う画像をアップしたときに、過去の文字起こし・翻訳結果のデータが画面に残ってるままでは奇妙だったので、新しい写真をアップした段階でその結果を消すために@click:clear="clearData"を書いています。

また、自動で利用者のブラウザの言語を読み取る部分に関しては、今回多言語化対応をしていますが、もし自分が英語話者だったとして、デフォルト言語が日本語だったらその時点で見る気失せますよね?
ということで、利用者の言語に合わせて表示を切り替える必要がありました。

そのためにmain.jsに以下を追加

main.js
const browserLanguage = function() {
  var ua = window.navigator.userAgent.toLowerCase();
  try {
    // chromeは以下で利用者のブラウザ言語を取得できる
    if (ua.indexOf('chrome') != -1) {
      return (
        navigator.languages[0] ||
        navigator.browserLanguage ||
        navigator.language ||
        navigator.userLanguage
      ).substr(0, 2);
    }
    // それ以外(例えばIEなど)は下記で取得する必要がある
    else {
      return (
        navigator.browserLanguage ||
        navigator.language ||
        navigator.userLanguage
      ).substr(0, 2);
    }
  } catch (e) {
    return undefined;
  }
};

//上記で取得した言語をデフォルト言語として設定
let defaultLang;
//今回日本語、英語、フランス語、ドイツ語、中国語のみに対応してるので、その中にブラウザで検出した言語が入ってるかどうか確認
const langs = ['ja', 'en', 'fr', 'de', 'zh']
if (langs.includes(browserLanguage())) {
  defaultLang = browserLanguage();
} else {
//もし上記の言語外の場合はデフォルト言語を英語にする
  defaultLang = 'en';
}

jsでブラウザの取得できるのは知ってたんですが、chromeしかやり方を知らなかったので、それ以外に対応させるのに色々と調べました。

また、条件文で以下のようにざっくりと書いたらうごくには動くんですが、ESLintでno-constant-conditionで怒られたので上記のように書き直しました。

if ( browserLanguage() == 'ja' || 'en' || 'fr' || 'de' || 'zh') {
  defaultLang = browserLanguage();
} else {
  defaultLang = 'en';
}

ほかは特に詰まることはなかったです。

工夫した点

今回はできるだけシンプルに!というのを目指して作りました。

なので、説明文はなくてもわかるように作りました。

結果、このようになりました。

スクリーンショット 2020-10-17 0.26.45.png

画像をドラッグすると、こんな感じで文字起こしする翻訳するのボタンが選べるようになります。

スクリーンショット 2020-10-17 0.27.55.png

また、翻訳言語に関しては、もちろん英語やドイツ語など多言語にその場で選ぶようにすることもできました。
が、画像を翻訳したいとなったときに、わざわざ母国語以外に翻訳するか?という気持ちから、母国語に翻訳できるようにということで、ボタン一つで表示の言語に翻訳という形にしました。

また、もし表示の言語と母国語が違う(翻訳したい言語が違う)という場合は右上のボタンから違う言語に変換することもできます。

後はコピーした後の動きなどを少し工夫。

これをこう。

そして右上の言語のボタンが何を表すのかわかりにくかなと思い、tootlipをその部分に利用。
スクリーンショット 2020-10-17 0.34.08.png

もちろんこの部分も多言語化対応済み。
例えば英語で見てるときは英語で表示されるようになっています。

こだわりたかったけど時間の都合で省略した部分

今回どうしても省略した部分としては、OGPの多言語化です。
これは諸々の設定があまりにも面倒だったので、代わりに日本語と英語を両方表示するようにしました。

スクリーンショット 2020-10-15 19.21.55.png

最後に

今回、作ったときはこんなに簡単なアプリを世の中に出してごめんなさい...という気持ちだったのですが、出してみると意外と好評で嬉しかったです。

これのLineBot版としてほんやくこんにゃくんも前に出したので、よかったら使ってみてください。

また、Product Huntにも出してます!良ければvoteお願いします。

https://www.producthunt.com/posts/pic-translator

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む