cssフレームワークのBULMAで text-align:center を実現したい場合は "has-text-centered"を使う
BULMAを使っていて text-align:center
はどうやるのか、一瞬迷った。
ドキュメント上で cetner
または text-align
などと検索しても出てこないので、ないのかなと一瞬思ったがそんなことはなく、探した方が悪かっただけだ。
BULMAでtext-align:centerを実現する場合は"has-text-centered"を使う
該当するドキュメントは下記となる。
https://bulma.io/documentation/helpers/typography-helpers/#alignment
実際にHTMLで記述する場合は
<div class="has-text-centered">hogehoge</div>
と指定することで中央に配置される。
余談: Bootstrapでtext-align:centerを実現する場合は "text-center"
ちなみにBootstrapで同じことをやりたい場合は text-center
となる。
<div class="text-center">hogehoge</div>
参照するドキュメントは下記
https://getbootstrap.jp/docs/5.0/utilities/text/#text-alignment
YouTube Premiumを3ヶ月無料で試せるキャンペーンの正体について調べてみた
YouTube Premiumをまだ試していない人は1ヶ月無料でYouTube Premiumを試すことができる。
だが、YouTube Premium
で検索すると、検索結果に3ヶ月無料でという文字が踊る。
(下記の画像を参照)
条件を満たしているなら3ヶ月無料で利用できるキャンペーンを用いたほうが当然お得である。
そこで今回はYouTube Premiumを3ヶ月無料で試せるキャンペーンについて調べてみた。
ちなみに私は既にYouTube Premiumであり、無料特典は1ヶ月無料で行っているので、今回紹介しているものを試すすべはない。
「ここで書いてあったから試してみたのにできなかったじゃないか!」
という苦情には答えられないので、試す際は一応ご自身でも調べてから実行してみてほしい。
(キャンペーン対象期間が過ぎていることも考えられるので)
ちなみにこのキャンペーンを行っているのは、Google Asia Pacific Pte. Limited
で、名前から察するにアジアを中心に展開しているキャンペーンなのではないだろうかと予測する(完全に自身の勝手な予想です)
目次
- 目次
- Google Oneを利用しているならYouTube Premiumを3ヶ月無料で試せる
- Chromebookを利用しているユーザならYouTube Premiumを3ヶ月無料で試せる
- Y!mobileの回線を利用しているユーザならYouTube Premiumを3ヶ月無料で試せる
- ソフトバンクユーザならYouTube Premiumを3ヶ月無料で試せる
- auを利用しているユーザならYouTube Premiumを3ヶ月無料で試せる
- Pokémon GOのトレーナーならYouTube Premiumを3ヶ月無料で試せる
Google Oneを利用しているならYouTube Premiumを3ヶ月無料で試せる
まず上の検索結果に登場してきたキャンペーンだが、これはGoogle One を利用しているユーザに対して実行されるキャンペーンのようだ。
(既に検索結果に書いてあるが)
YouTube Premium を 3 か月間無料でお楽しみいただけます | YouTube
ただGoogle Oneを利用しているユーザに対して、どうやれば3ヶ月無料で試せるようになるのかが分からない。
上のキャンペーンページからの誘導も特にないので、これは完全に置いてけぼりを食らうパターンではないだろうか。
Googleは一見便利そうなキャンペーンを行っていても、それを実現させるまでの道のりが非常に分かりにくいというパターンが割とよくある印象。
もし私が村上春樹の小説の主人公なら、ここは「やれやれ」とつぶやくところだろう。
やれやれ
Chromebookを利用しているユーザならYouTube Premiumを3ヶ月無料で試せる
上の特典内容と若干似ているが、Chromebookを利用しているユーザならYouTube Premiumを3ヶ月無料で試せるようだ。
こちらキャンペーンページが用意されており、YouTube Premiumの特典についても下記のページからたどり着けるようになっている。
Chromebook の特典 - Google Chromebooks
試しに私がMacBookからアクセスしてみたところ、下記のような画面になったので、後はきみの目で確かめてみてくれ!
Y!mobileの回線を利用しているユーザならYouTube Premiumを3ヶ月無料で試せる
次はGoogle以外が主催しているキャンペーン。Y!mobile。
Y!mobileの回線を利用しているユーザがそのSIMカードを挿入したiPhone、スマートフォンまたはタブレットから YouTube Premium に申し込むと、3ヵ月無料で利用できるようだ。
詳細は下記のキャンペーンページを確認してほしい。
YouTube Premium 3ヵ月無料キャンペーン|Y!mobile - 格安SIM・スマホはワイモバイルで
ソフトバンクユーザならYouTube Premiumを3ヶ月無料で試せる
こちらはソフトバンクユーザ向けのキャンペーン。
上のY!mobileと同じく、ソフトバンクの回線を利用しているユーザがそのSIMを設定した iPhone、iPad、Google Pixel、その他Android搭載のスマートフォンまたはタブレットから YouTube Premium に申し込むと、3ヵ月無料で利用できるらしい。
YouTube Premium 3ヵ月無料キャンペーン | スマートフォン・携帯電話 | ソフトバンク
auを利用しているユーザならYouTube Premiumを3ヶ月無料で試せる
auからの加入でも同じように3ヶ月無料キャンペーンが試せる。
YouTube Premiumのご紹介 | エンタメ | au
Pokémon GOのトレーナーならYouTube Premiumを3ヶ月無料で試せる
いくつか通信業者経由でのキャンペーンを紹介してきたが、次はPokémon GO。
Pokémon GOのトレーナーならYouTube Premiumを3ヶ月無料で試せるらしい。
詳細は下記のページを見てみてほしい。
トレーナーの皆さん:YouTube Premium 3ヶ月分をプレゼント! – Pokémon GO
というわけで、YouTube Premiumを3ヶ月無料で試せるキャンペーンについて興味本位で調べてみた。
私自身は既にYouTube Premiumを利用しているため、これらのお得なキャンペーンの恩恵にあずかることはできないが、もしまだYouTube Premiumを利用していない方でこれらの条件にあう方は試してみてほしい。
また、他にもこんなキャンペーンがあるよ、というのがあればコメントください。
Googleが検索結果にAMP表示を出すことをやめたことについて。また、Habanero Beeの今後の対応について。
モバイルの検索結果からAMP対応を示す雷マークが消えた
9to5Googleの下記の記事によると、Googleの検索結果からAMPに対応していることを表す雷マークを削除したようだ。
確かに実際に検索してみると、雷マークが表示されなくなっている。
(自身のサイトを例に取り恐縮だが、下記の自身のサイトはAMP対応している。以前だったらAMP対応済みであることを示す雷マークが表示されていたが、現在は既に検索結果から消えている。)
※なお、上のキャプチャはChromeのモバイル表示でスクショしているが、iPhoneのSafariから検索しても同様の表示となっていることを確認している
ちなみにAMPのアイコンは消えているものの、検索結果からアクセスするとAMPページにはアクセスされる。
ここの挙動については変更はされていない。
なお、Googleはこの変更について4月に発表していた。
すでにインターネットのメディアで取り上げられていたのを見て、知っている方も多いだろう。
AMP対応する意味はなくなったのか?
さて、このようにGoogleの検索結果からはAMP対応かどうかは分からないような変更が加わったが、ではそもそもAMP対応することの意味はなくなったと言えるだろうか、というと、
まだ、AMP対応する意味はある(と思いたい)。
そもそもの話だが、AMP対応自体の根本的な意義としては、ウェブのベストプラクティスをサイト側に行わせるための施策である。
よってAMPに対応しているということは、必然的にパフォーマンスの良い使用感に優れたサイトであることは間違いない。
(もちろん代償としてはJavaScriptを利用したリッチな表現ができなかったり、それなりに大きい)
そのためAMPに対応すること自体の意味はなくなっていないと私は考えている。
ただ、検索結果のAMPアイコンなどは多少は、AMP非対応のサイトとの(見た目的な意味での)差別化要因となっていたので、これが今後クリック率にどう影響を与えるかはチェックしておいたほうが良いと思われる。
私の個人的な意見としては、AMPにきっちり対応しなくとも、ある程度パフォーマンスに優れたサイトを構築できれば、それでいいのかも知れないと考えている。
(AMP対応することによる制約の大きさによる苦労は、Habanero Beeの開発を通じてそれなりに味わった)
Habanero Beeの今後の対応方針
最後に私が開発しているHabanero Beeについての今後の対応方針について述べていく。
※Habanero Beeとはなんぞや?という方は下記の記事を読んでください。
現状では、AMP対応のサイトを構築できるというメリットの打ち出しをやめる気はない。
上でも書いたとおり、AMPに対応できるということは必然的にある程度のパフォーマンスを確保できることと同義だからだ。
そのため、現状開発の方針を変える気はないが、引き続きAMPに関する話題は追っていく予定である。
AMP対応することの大変さ
Habanero BeeはAMP対応を打ち出しているが、やはりというか、実際に開発してみて思い知ったのはAMP対応することによる制約の大きさだ。
リッチな表現ができないぐらいであれば許容できる範囲だったが、Google AdSenseや新しいGoogle Analytics(GA4)に対応していないなど、Googleが関わっているプロジェクトなのにも関わらずAMPに関する対応は明らかに不足している。
(おそらく中の人も大変なのだろうとは思う)
これまではAMP対応することによるメリットを天秤にかけた上でこれらのことには目をつむってきたが、今後AMP対応に関するメリットよりもこれらの不満のほうが大きくなればHabanero Beeの方針を変更するかも知れない。
(もしくは似たようなプロジェクトを別に作成するかも知れない。どちらがいいのだろう?AMP対応がさくっと行えることは一つのアドバンテージであると思うが、それが未来永劫続いていくわけではないだろう)
というわけで、今のところHabanero Beeの方針を変更する予定はないが、今後のAMPに関する状況、またモバイル検索結果に関する何かしらの変更があれば、都度都度考えては行くと思う。
BootstrapのBorder spinner (ローディングスピナー) が動かないときにチェックすること(Bootstrap v4)
Bootstrap(v4)の下記のページを参考にしてBorder spinnerを表示させようとした際、そのままコードをコピペしただけでは動かなかった。
本来ここに表示されているコードをそのままコピペするだけで動くはずだが、コピペしてもローディングスピナーは表示されない。
はて?と疑問に思ったが、読み込んでいるBootstrapのversionが古かったのが原因のようだ。
下記は読み込んでいるBootstrapのバージョンを修正した際のdiff。
diff --git a/public/index.html b/public/index.html --- a/public/index.html +++ b/public/index.html @@ -26,8 +26,8 @@ --> <link rel="stylesheet" - href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" - integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" + href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" + integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" /> <script
このようにv4の最新版( こちら )のversionのBootstrapを読み込むようにすることでローディングスピナーが表示されるようになった。
娘の記録(育児日記メモ)
最近の娘のことを簡潔に書いていく。
数年後、この文章を読んで、「こんなこともあったな〜」と思い返せるような未来の自身に当てた育児日記的な役割を果たすことになる。
保育園に通い出した
今年度から娘は保育園に通いだしている。
最初の方は保育園に送りに行くと泣き出すこともあったものの、すぐに慣れて、今では送っていくと保育園の先生にじゃれついたり、おもちゃを持って遊びだしたりして楽しそうだ。
保育園に通いだしてから体調を崩すようになった。
これは 育児あるある
のようで、保育園で他の園児から菌をもらったりするなどして、すぐに体調を崩してしまうようだ。
現にいまもRSウィルスというのが流行っており、半数近くの園児たちが休んでいる。
(幸いにも娘は大丈夫なようだが)
なお、娘が体調を崩すようになったのにつられて、我々も体調を崩すようになった。
娘が鼻水と咳を出すようになると、数日後には私も同じ症状が出る、といった形で、明らかにうつされている形だ。
こういう親御さんは多いらしい。なるほど、子供の症状は親にもきっちり伝染るのだ。
娘は頑固&破天荒。そしておしゃべり
これは生まれた直後から病院の方々にも言われていたことだが、娘は意思がはっきりしている子供で、「自分が一度決めたことはやり抜くまでやめないし、なにか言われても頑なに意思を変えることは絶対にしない性格」である。
それは今も健在で、超がつくほど頑固である。
こういうのは大変な反面、まあ頼もしいというか、将来起業家にでもなりそうな雰囲気を感じるが、まあこういうのはどこにでもある『親バカ思考』なのだろうなと妻と笑うことがある。
あと遊んでいたぬいぐるみに対していきなり冷徹な攻撃を食らわしたり、少しサイコパスっている一面も見せているのが、まあ大丈夫だろう。
そして元気いっぱいな破天荒ぶりも見せる。
娘は基本的に自分で何もかもしたいタイプのようで、外に行くときも抱っこをするとキレる。
自分で歩くのは問題ないが、危なっかしく階段を登り始めたり、いきなり走り出して転んで泣く、という行為を3秒ぐらいの低スパンで実現させるなど、「目が離せない行動」が多い。
さすがに道路付近では手は絶対に離さないが、まあ異常に行動的な側面を持っており、目が離せないことが多い。
そして娘はかなりおしゃべりだ。
起きているあいだじゅう、ずっとおしゃべりしながら何かをしている。
体は小さいのに、よくスタミナが尽きないな〜といった感じである。
これは保育園の先生からもよく言われることで、
「〇〇ちゃんはずっとおしゃべりしていますね〜」
とよく言われる。
日中、そんな感じでずっとフルスロットルで生きているため、眠りだすと、もう深く眠る。
夜泣きをすることが殆どない。
そういう点で我々夫婦は恵まれているのかも知れない。
娘が唐突にアンパンマンという言葉を発するようになった
— Yuki Shindo (@shinshin86) July 3, 2021
少し前にもツイートしたが、そう言えば最近は様々な言葉を発するようになった。
これからいろいろな言葉を発していくようになるだろう。
(そう言えば少し前に急に私のことを「先生」と呼び始めて何事かと思った)
そんなわけで娘の記録は以上となる。
健やかにこのまま育っていってくれ。
Reactを自作するための学習メモ #5
Reactを自作するための学習の記録の続き
この続き
参照ドキュメント
現在ここに記述しているコードは、下記のドキュメントのほぼ写経となっています。
関数コンポーネントに対応する
現在の実装では関数コンポーネントに対応していない。
例えば下記のようなコードを書いた場合、ブラウザアクセス時にエラーが発生する。
import { createElement, render } from "./Didact"; /** @jsxRuntime classic */ /** @jsx createElement */ const App = props => ( <div> <h1>Hi {props.name}</h1> <ul> <li>aaa</li> <li>bbb</li> <li>ccc</li> </ul> </div> ) const element = <App name="foo" /> const container = document.getElementById("root") render(element, container)
エラーの内容はこんなやつ
Uncaught DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('props => Object(_Didact__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", { __self: undefined, ・ ・ ・
関数コンポーネントは、クラスコンポーネントとは下記のような異なる点があるため、現在の実装では正常に処理が行えない。
- 関数コンポーネントからのファイバーにはDOMノードが存在しない
- 子要素は、propsから直接取得するのではなく、関数を実行することから取得できる
そのため、 performUnitOfWork
関数でファイバーの型が関数かどうかを確認して、処理を振り分けるようにする必要がある。
以前と同じ処理は、updateHostComponent
で実行するようにし、関数コンポーネントの場合は、 updateFunctionComponent
で処理を実行するようにコードを追加していくことにする。
(参照元のドキュメントに倣っています)
渡されたコンポーネントが関数だった場合、 updateFunctionComponent
では関数を実行して子を取得するようにしていく。
子要素を取得できれば、差分検出の処理などは同じように機能するため、以後の処理に変更を加える必要はない。
ただし、commitWork
関数については変更の必要がある。
関数コンポーネントの場合、DOMノードがないfiberであるため、DOMノードの親を見つける必要がある。
よって、下記のようなコードとなる。 (説明放棄)
const createElement = (type, props, ...children) => { return { type, props: { ...props, children: children.map(child => typeof child === "object" ? child : createTextElement(child) ), } }; } const createTextElement = text => { return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, } } const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) const isEvent = key => key.startsWith("on") const isProperty = key => key !== "children" && !isEvent(key) const createDom = fiber => { const dom = fiber.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(fiber.type) updateDom(dom, {}, fiber.props) return dom } const updateDom = (dom, prevProps, nextProps) => { // Remove or modify event listeners Object.keys(prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.removeEventListener( eventType, prevProps[name] ) }) // Delete old properties Object.keys(prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // Set a new or changed property Object.keys(nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // Add a new event listener Object.keys(nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.addEventListener( eventType, nextProps[name] ) }) } const render = (element, container) => { wipRoot = { dom: container, props: { children: [element] }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } let wipRoot = null let currentRoot = null let nextUnitOfWork = null; let deletions = null const commitRoot = () => { commitWork(wipRoot.child) currentRoot = wipRoot wipRoot = null } const commitWork = fiber => { if (!fiber) { return } // DOMノードを持つファイバーが見つかるまでファイバーツリーを上に移動 let domParentFiber = fiber.parent while (!domParentFiber.dom) { domParentFiber = domParentFiber.parent } const domParent = domParentFiber.dom if (fiber.effectTag === "PLACEMENT" && fiber.dom != null) { domParent.appendChild(fiber.dom) } else if (fiber.effectTag === "UPDATE" && fiber.dom != null) { updateDom(fiber.dom, fiber.alternate.props, fiber.props) } else if (fiber.effectTag === "DELETION") { commitDeletion(fiber, domParent) // ノードを削除するときは、DOMノードを持つ子が見つかるまで探索を続行 } commitWork(fiber.child) commitWork(fiber.sibling) } const commitDeletion = (fiber, domParent) => { if (fiber.dom) { domParent.removeChild(fiber.dom) } else { commitDeletion(fiber.child, domParent) } } const workLoop = deadline => { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork( nextUnitOfWork ) shouldYield = deadline.timeRemaining() < 1 } if (!nextUnitOfWork && wipRoot) { commitRoot() } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) const performUnitOfWork = fiber => { // 関数かどうかを確認 const isFunctionComponent = fiber.type instanceof Function if (isFunctionComponent) { updateFunctionComponent(fiber) } else { updateHostComponent(fiber) } if (fiber.child) { return fiber.child } let nextFiber = fiber while (nextFiber) { if (nextFiber.sibling) { return nextFiber.sibling } nextFiber = nextFiber.parent } } const updateFunctionComponent = fiber => { const children = [fiber.type(fiber.props)] reconcileChildren(fiber, children) } const updateHostComponent = fiber => { if (!fiber.dom) { fiber.dom = createDom(fiber) } reconcileChildren(fiber, fiber.props.children) } const reconcileChildren = (wipFiber, elements) => { let index = 0 let oldFiber = wipFiber.alternate && wipFiber.alternate.child let prevSibling = null while (index < elements.length || oldFiber != null) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && element.type === oldFiber.type // If the old fiber and the new element are of the same type, keep the DOM node and update it with the new props if (sameType) { // Update process for nodes newFiber = { type: oldFiber.type, props: element.props, dom: oldFiber.dom, parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } // If the type is different and there is a new element, a new DOM node needs to be created if (element && !sameType) { // Adding a node newFiber = { type: element.type, props: element.props, dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } // If different types and old fibers are present, delete the old node if (oldFiber && !sameType) { // Deletion process of old fiber nodes oldFiber.effectTag = "DELETION" deletions.push(oldFiber) } if (oldFiber) { oldFiber = oldFiber.sibling } if (index === 0) { wipFiber.child = newFiber } else { prevSibling.sibling = newFiber } prevSibling = newFiber index++ } } export { createElement, render }
useState(hooks)に対応させる
関数コンポーネントの対応の次は useState
にも対応させる。
クリックするたびに、stateのカウンター値が1つ増えるような、サンプルとしてよくある例にコードを変更する。
index.js
を下記のように変更して、これが動くようにDidact.js
を修正していく。
// index.js import { createElement, render, useState } from "./Didact"; /** @jsxRuntime classic */ /** @jsx createElement */ const App = () => { const [ count, setCount ] = useState(1) return ( <h1 onClick={() => setCount(count => count + 1)}> Count: {count} </h1> ) } const element = <App name="foo" /> const container = document.getElementById("root") render(element, container)
関数コンポーネントが useState
を呼び出すとき、既に古いフックがあるかどうかを確認し、 hookIndex
を用いてwipFiber.alternate
をチェックするようにする。
もし古いフックがある場合は、stateを古いフックから新しいフックにコピーする。そうでない場合は初期化。
新しいフックをファイバーに追加する際は、 hookIndex
を1つインクリメントしてstateを返すようにする。
また useState
はstateを更新する関数を返す必要があるため(ここでは setCount
関数)、アクションを受け取る setState
関数を定義する。
そのアクションをフックに追加したキューにpushする。
そして、作業のループが新しいレンダリングフェーズを開始できるように、新しい wipRoot
を次の作業単位として設定する。
ここらへんのコードは下記のように実装。
(何度もしつこいですが、参照元の写経です)
const useState = initial => { const oldHook = wipFiber.alternate && wipFiber.alternate.hooks && wipFiber.alternate.hooks[hookIndex] const hook = { state: oldHook ? oldHook.state : initial, queue: [] } const actions = oldHook ? oldHook.queue : []; actions.forEach((action) => { hook.state = action(hook.state); }); const setState = action => { hook.queue.push(action) wipRoot = { dom: currentRoot.dom, props: currentRoot.props, alternate: currentRoot, } nextUnitOfWork = wipRoot deletions = [] } wipFiber.hook.push(hook) hookIndex++ return [hook.state, setState] } ・ ・ ・ export { createElement, render, useState }
最後に
ひとまずドキュメントを写経しながらという形でReact自作に向けた学習を進めてきた。
が、一旦この学習はここで中断する。
他にやりたいタスクが残っているのと、Reactを1から自作するというのはなかなか時間がかかりそうだということがわかり、それに対して今は時間を確保できなそうだからだ。
あと、Fiber周りの仕組みなどがまだまだ理解が追いついていないので、ここについてはもう少し復習しても良さそうに思える。
実際のReactのコードも参照しつつ進めていたが、React本家のコードを細かく追っていくのもなかなかに骨が折れる作業だということも分かった。
ただ、Reactがどういう仕組で実際にHTMLを生成していくのか、差分検出はどのような考え方で実行されているのか?など今までほぼほぼブラックボックスとなっていた中身が想像できるようになったのは、今後もReactを触っていく上では大きいことのように思う。
また時間を見つけてReact自作に向けた学習は進めていく予定。
なお、ここまでのコードは下記に載せている。
後々またこのリポジトリにコードを追加していくかもしれないので、コミットログと併せてご参照ください。
(この時点での最新のコードはコミットハッシュが 23bc60265d2a1813f1751708536d4b4d9298eecc
となっています)
MDN Web Docsへのコントリビュート手順(備忘録)
これはMDN Web Docsへのコントリビュートを実際に行った際の手順などをメモした備忘録となる。
目次
- 目次
- アカウントを作成(これは不要かもしれない)
- コントリビューションに関するMDNのドキュメント
- 英語以外のドキュメントにコントリビューションしたいとき
- 修正対象のファイルの探し方
- ローカルサーバで修正したドキュメント内容を確認する方法
- 実際にコントリビューションしてみた
アカウントを作成(これは不要かもしれない)
MDN Web Docsにアクセスすると、画面右上にあるSigninからアカウントを作成できる。
これが必須なのかはわからないが、とりあえずアカウントは作成してみた。
GitHub連携かGoogle連携か選べたので、GitHub連携で作成してみた。
ただ、その後の作業手順を考えると、別にコントリビュートをするのにアカウントは作らなくても良かったのではないかとも思ったりした。
(コントリビューション自体はGitHubアカウントがあればできそうだったので)
コントリビューションに関するMDNのドキュメント
コントリビューションに関するMDNのドキュメントは下記に記載されている。
まずはこちらを読んだ。
content/README.md at main · mdn/content · GitHub
MDNへコントリビューションする際はGitHubアカウントが必須
修正作業はGitHub経由で行われるのでアカウントを持っていない方はアカウントを作成するところから始める必要がある。
MDN の構成について
MDNのドキュメントとGitHubで格納されている実際のファイルの関係については下記に記載がある。
content/README.md at main · mdn/content · GitHub
英語以外のドキュメントにコントリビューションしたいとき
ちなみに上記リポジトリは英語のドキュメントのみを扱っている。
他の言語のドキュメントについてコントリビューションを行いたい場合は、下記のリポジトリを見る必要がある。
GitHub - mdn/translated-content: All translated MDN content in raw form
なお、現在は下記の言語しかコントリビューションを受け付けていないようだ。
これはアクティブなメンテナンスチームの言語のみを表しているとのこと。
- fr
- ja
- ko
- pt-BR
- ru
- zh (zh-CN and zh-TW)
修正対象のファイルの探し方
上の MDN の構成について
でも書いたが、URLを見ながら実際に修正対象となるファイルを探していくことになる。
MDN Web Docsへコントリビューションする際に事前に知っておくべき情報は、これで以上となる。
ここからは実際にリポジトリで修正作業を行った際、修正内容をローカルで確認する方法について記載していく。
ローカルサーバで修正したドキュメント内容を確認する方法
1. まずは下記のリポジトリをどちらもcloneする
下記がMDN Web Docsの本体(英語のみ)
下記がMDN Web Docsの多言語に関するファイルが格納されている。
2. content repoのセットアップ
clone
した2つのリポジトリのうち、content
のルートで下記のコマンドを打って依存パッケージをインストールする
(ちなみに Node.js
のインストールと、 yarn
のインストールは事前に済ませておくこと)
yarn install
3. 環境変数を追加
cloneしていたもう一つのリポジトリ translated-content
のfiles
のパスを CONTENT_TRANSLATED_ROOT
にセットした状態で yarn start
を行う
実際に打つ際のコマンドは下記。
CONTENT_TRANSLATED_ROOT=/path/to/translated-content/files yarn start
もしくは export
で環境変数を設定してからでも良い。
export CONTENT_TRANSLATED_ROOT=/path/to/translated-content/files yarn start
すると、ローカルサーバが立ち上がるので、 localhost:5000
にアクセスして確認が可能。
(アクセスした画面から指定のページに遷移し、 Change Language
から指定の言語に変更できる)
環境変数追加の別の方法
上のやり方以外にも .env
ファイルを用いて設定する方法もある。
こちらであれば以降はyarn start
だけでOKとなる。
echo CONTENT_TRANSLATED_ROOT=/path/to/translated-content/files >> .env # .envに設定情報は記憶されるので、あとは "yarn start"でOK yarn start
ローカルでの確認方法については以上である。
実際にコントリビューションしてみた
今回修正しようと思ったファイルはこちらである。
日本語の誤字を見つけたので、その修正PRを作成することにした。
まずは上にも書いたtranslated-content
のリポジトリを自身のGitHubアカウントにforkする
次は人によるかもしれないが、私はローカルに一度cloneしてから修正PRを作成、
上で書いたローカルサーバを立ち上げる方法で修正内容を確認したりしながら作業を実施。
実際に作成したPRが下記となる。
修正で凡ミスをしてしまっており恥ずかしい。
またPRを見てもらうと分かる通り、日本人の方に対応していただき、コミュニケーションは日本語で行うことができた。
(まだ一度PRを作っただけのため、毎回、日本語で行えるかどうかまでは分からない)
またPRのやり取りを見てもらうと分かる通り、実は日本語のドキュメントには修正必要な箇所や、不要なfont
タグがソース内に散りばめられており(自動生成した際の痕跡とか?)、修正が必要な箇所は結構ありそうでした。
なお、日本語のドキュメントを修正する際は下記のガイドラインにも目を通しておくと良さそうだ。
というわけで、もしこの記事がMDNへコントリビューションしようか迷っている方の背中を押すきっかけになれば幸いです。
ちなみにMDNのPRを作成すると、最初に push
した分の修正に対してPreview URLが発行されるので、そちらで一度修正内容を確認してみるのも良さそうです。
(自分はそこで確認するのも漏れていたので、これは自分に対する戒めでもあります。)