Run `msgunfmt messages.mo -o messages.po` from GNU gettext. A `.mo` stores only the header and the source/translation pairs, so the recovered `.po` has no translator comments, source references, fuzzy or obsolete entries. In Python, `polib.mofile()` plus `save_as_pofile()` does the same.
Extract a `.pot` template with `xgettext` or your framework's command, create a language file from it with `msginit`, and after code changes refresh each `.po` with `msgmerge --update`. Compile to `.mo` with `msgfmt`. Edit `.po` files by hand or in Poedit, never the `.mo`.
Since Java 9 `PropertyResourceBundle` reads `.properties` files as UTF-8 and falls back to ISO-8859-1 when the bytes are not valid UTF-8. On Java 8 the files are ISO-8859-1, so use `\uXXXX` escapes or load them through a `ResourceBundle.Control` with a UTF-8 reader.
Put `%{name}` in the YAML value and pass `name` as a keyword argument to `t`. A `count` argument selects the `one`/`other` plural form and is interpolated as well. A missing variable raises `I18n::MissingInterpolationArgument`.
Use `\n` in a double-quoted value or a block scalar (`|` keeps line breaks, `>` folds them). Quote values with a space-hash or colon-space sequence, values starting with `*`, `&`, `!`, `%`, `[` or `{`, and `yes`, `no`, `on`, `off` or `null`, which Ruby's YAML 1.1 parser reads as booleans or nil.
Split `config/locales` by concern, such as `models/user/en.yml`, `views/users/en.yml` and `defaults/en.yml`; Rails 7.0+ loads subdirectories automatically. Name view keys after the view path so `t(".title")` works, and keep model and attribute names under `activerecord`.