「まだRailsでstrftime使ってるやついる？」「いねえよなぁ！！？」【日時のフォーマットをja.ymlで管理する】

※ 結論だけ知りたいという人は[解決法](#解決法)から見てください

「まだRailsでstrftime使ってるやついる？」

「いねえよなぁ！！？」

...
...

はい、使ってます。

`strftime`を使用して、日時をフォーマットする際
``` erb:View
<p class="time"><%= @model.time.strftime('%Y年%m月%d日 %H時%M分') %></p>
```
のように毎回記述する必要があり
「いちいち指定しなくても同じ形式でフォーマットしてほしい」
「フォーマットをまとめて管理したい」
といった悩みに苦しめられてきました。

「仕方ない」と諦めていたこの悩み...解決する方法を実は見つけてしまったのです。

私みたいに諦めてstrftimeを使っているそこの貴方、今まさにstrftimeを使おうとしているそこの貴方。
「もっと早く気づいていれば、もっといい感じにプロダクトを作れた」
と思うその前に、是非参考にしてみてください。

# strftimeとは？
strftimeは、日時のデータを指定したフォーマット文字列に変換できるメソッドです。
詳しい使い方は[Rubyリファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/method/Time/i/strftime.html)を参照してください。

# strftimeを使いたくない理由
日時のフォーマットで検索するとよく出てくるstrftimeですが、 使用する時には
`@model.time.strftime('%Y年%m月%d日 %H時%M分')`
のような記述を繰り返し記述する必要があります。
全て`%Y年%m月%d日 %H時%M分`のフォーマットで表示したいだけにも関わらず、同じ内容を繰り返し書くことになるので、コードが[DRY](https://ja.wikipedia.org/wiki/Don%27t_repeat_yourself)な状態とは言えません。
「日時の表示を別の形式に変えて欲しい！」
といった急な要望が来た時

- 修正に時間がかかる
- 一部修正が漏れていた

といった問題が起きやすくなります。

# 解決法
**ja.ymlにフォーマットを記述して、lメソッドを使って呼び出す**

今回の問題は、これだけで解決します。
具体的な設定方法をこれから説明します。

## デフォルトのlocaleをjaに設定
railsは`config/locales`というフォルダの中で翻訳したい各国語の文言を管理しています。`locale`は元々、多言語化用に使われるものでした。多言語化以外にも、バリデーションの文言を管理したり、各カラムの日本語名を管理したりと、用途は多岐に渡っています。

今回は、`config/application.rb`でデフォルトの言語を`:ja`（日本語）に指定します。
この設定を入れることで、`config/locales/ja.yml`の中で言語を管理するようになります。
デフォルト言語を`:ja`に設定して、`ja.yml`で言語管理していくことは定番の方法なので、覚えておきましょう。
``` config/application.rb
config.i18n.default_locale = :ja
```

## ja.ymlに指定したいフォーマットを記述
`config/locales/ja.yml`に指定したいフォーマットを記述します。`ja.yml`がない場合は、作成してください。
``` config/locales/ja.yml
ja:
  date:
    formats:
      default: '%Y/%-m/%-d'
  time:
    formats:
      default: '%Y-%-m-%-d %H:%M:%S'
```
`Date`型のフォーマットは、`date:`以下に指定したものが使用されます。
`Time`、`DateTime`、`TimeWithZone`型のフォーマットは、`time:`以下に指定したものが使用されます。
`Date`型に対して`time:`のフォーマットは利用できず、エラーになります。逆も同様です。両方とも設定することをオススメします。
`default:`に指定したフォーマットが標準になります。ご自身のアプリケーションの中で一番使われているフォーマットを指定してください。

## lメソッドを使用して、フォーマットを反映する
`l`メソッドは、I18nモジュールに用意されている`localize`メソッドのエイリアスで、日付や数値などを`locale`に指定されているフォーマットに変換します。
```erb:View
<%= l @model.time %> <%#=> 2022-06-10 16:30:40 %>
```
ViewやControllerでは、`l`メソッドがヘルパーメソッドとして用意されているので、`l`だけで呼び出すことができます。
それ以外の場所では、`I18n.l`で呼び出してください。
```rb:Model
# Date
I18n.l(Date.today) #=> '2022/6/10'
# Date
I18n.l(Time.current.to_date) #=> '2022/6/13'
# Datetime
I18n.l(DateTime.now) #=> '2022-6-10 16:30:40'
# Time
I18n.l(Time.now) #=> '2022-6-10 16:30:40'
# TimeWithZone
I18n.l(Time.current) #=> '2022-6-10 16:30:40'
# ヘルパーが用意されていないところでlで呼び出すと、NoMethodErrorになる
l(Time.current) #=> NoMethodError (undefined method `l' ...
```
`l`メソッドを使うことで、`strftime`で必要だったフォーマット指定が必要なくなります。

## 応用： default以外のフォーマットを指定する
運用していく中で、日時の表示方法は一つとは限りません。
「このページは年の表示要らない」「このページだけ日本語で月とか日って表示したい」
といった時、`default`以外のフォーマットを作成し、フォーマットを切り替えることが可能です。
``` config/locales/ja.yml
ja:
  date:
    formats:
      default: '%Y/%-m/%-d'
      long: '%Y年%-m月%-d日(%a)'
    abbr_day_names: [日, 月, 火, 水, 木, 金, 土]
  time:
    formats:
      default: '%Y-%-m-%-d %H:%M:%S'
      short: '%m-%d %H:%M'
```
``` erb: View
<%= l @model.time, format: :short %> <%#=> 06-10 16:30 %>
```
``` rb: Ruby
I18n.l(Date.today, format: :long) #=> '2022年6月10日(金)'
I18n.l(Time.current, format: :short) #=> '06-10 16:30'
# Date型にtimeでしか指定していないフォーマットを使うとエラーになる
I18n.l(Date.today, format: :short) #=> I18n::MissingTranslationData (translation missing: ja.date.formats.short)
# TimeWithZone型にdateでしか指定していないフォーマットを使うとエラーになる
I18n.l(Time.current, format: :long) #=> I18n::MissingTranslationData (translation missing: ja.time.formats.long)
```
# まとめ
`strftime`だけを用いて都度日時のフォーマットをしていくと、コードがDRYな状態ではなくなっていきます。

- 対象の`locale`のymlに指定したいフォーマットを記述
- `l`メソッドを用いてView表示する

この2つのことをするだけで、日時のフォーマットをまとめて管理でき、よりDRYなコードに近づきます。
たくさんの`strftime`修正に悩まされるその前に、是非今回の方法を試してみてください。