Loading repository data…
Loading repository data…
fnando / repository
It's a small library to provide the I18n translations on the Javascript. It comes with Rails support.
gem install i18n-js
Or add the following line to your project's Gemfile:
gem "i18n-js"
Create a default configuration file in ./config/i18n.yml
i18n init
About patterns:
* as a wildcard and can appear more than once.
* will include everything*.messages.*! are excluded.
!*.activerecord.* will exclude all ActiveRecord translations.{pt-BR,en}.js.* will include only pt-BR and en translations, even if
more languages are available.[!NOTE]
Patterns use glob, so check it out for the most up-to-date documentation about what's available.
The config file:
---
translations:
- file: app/frontend/locales/en.json
patterns:
- "*"
- "!*.activerecord.*"
- "!*.errors.*"
- "!*.number.nth.*"
- file: app/frontend/locales/:locale.:digest.json
patterns:
- "*"
The output path can use the following placeholders:
:locale - the language that's being exported.:digest - the MD5 hex digest of the exported file.The example above could generate a file named
app/frontend/locales/en.7bdc958e33231eafb96b81e3d108eff3.json.
The config file is processed as erb, so you can have dynamic content on it if you want. The following example shows how to use groups from a variable.
---
<% group = "{en,pt}" %>
translations:
- file: app/frontend/translations.json
patterns:
- "<%= group %>.*"
- "!<%= group %>.activerecord.*"
- "!<%= group %>.errors.*"
- "!<%= group %>.number.nth.*"
Your i18n yaml file can be exported to JSON using the Ruby API or the command line utility. Examples of both approaches are provided below:
The Ruby API:
require "i18n-js"
# The following call performs the same task as the CLI `i18n export` command
I18nJS.call(config_file: "config/i18n.yml")
# You can provide the config directly using the following
config = {
"translations"=>[
{"file"=>"app/javascript/locales/:locale.json", "patterns"=>["*"]}
]
}
I18nJS.call(config: config)
#=> ["app/javascript/locales/de.json", "app/javascript/locales/en.json"]
The CLI API:
$ i18n --help
Usage: i18n COMMAND FLAGS
Commands:
- init: Initialize a project
- export: Export translations as JSON files
- version: Show package version
- plugins: List plugins that will be activated
- lint:translations: Check for missing translations
- lint:scripts: Lint files using TypeScript
Run `i18n COMMAND --help` for more information on specific commands.
By default, i18n will use config/i18n.yml and config/environment.rb as the
configuration files. If you don't have these files, then you'll need to specify
both --config and --require.
embed_fallback_translations:Embed fallback translations into each locale. This can be useful in cases where you have multiple large translation files and don't want to load the default locale together with the target locale.
When I18n.fallbacks is configured (e.g. via I18n::Backend::Fallbacks or
Rails' config.i18n.fallbacks), the full fallback chain is respected. For
example, if es falls back to pt which falls back to en, missing es
translations are first filled from pt, then from en. When I18n.fallbacks
is not configured, translations fall back directly to I18n.default_locale.
To use it, add the following to your configuration file:
---
pipeline:
- plugin: embed_fallback_translations
enabled: true
[!NOTE]
I18n.fallbacks=(*)is weird. The most common way of using it is withI18n.fallbacks[:es] = [:pt, :en]andI18n.fallbacks = I18n::Locale::Fallbacks(default_locale, hash_map). If you assign just a hash, no error will be raised, but it's not the correct behavior, and you'll get a hash back, rather than aI18n::Locale::Fallbacksinstance.When assigning an array, remember to pass the default locale as the last argument, as precedence is from left to right.
export_files:By default, i18n-js will export only JSON files out of your translations. This plugin allows exporting other file formats. To use it, add the following to your configuration file:
---
pipeline:
- plugin: export_files
enabled: true
files:
- template: path/to/template.erb
output: "%{dir}/%{base_name}.ts"
You can export multiple files by defining more entries.
The output name can use the following placeholders:
%{dir}: the directory where the translation file is.%{name}: file name with extension.%{base_name}: file name without extension.%{digest}: MD5 hexdigest from the generated file.The template file must be a valid eRB template. You can execute arbitrary Ruby code, so be careful. An example of how you can generate a file can be seen below:
/* eslint-disable */
<%= banner %>
import { i18n } from "config/i18n";
i18n.store(<%= JSON.pretty_generate(translations) %>);
This template is loading the instance from config/i18n and storing the
translations that have been loaded. The
banner(comment: "// ", include_time: true) method is built-in. The generated
file will look something like this:
/* eslint-disable */
// File generated by i18n-js on 2022-12-10 15:37:00 +0000
import { i18n } from "config/i18n";
i18n.store({
en: {
"bunny rabbit adventure": "bunny rabbit adventure",
"hello sunshine!": "hello sunshine!",
"time for bed!": "time for bed!",
},
es: {
"bunny rabbit adventure": "conejito conejo aventura",
bye: "adios",
"time for bed!": "¡hora de acostarse!",
},
pt: {
"bunny rabbit adventure": "a aventura da coelhinha",
bye: "tchau",
"time for bed!": "hora de dormir!",
},
});
You can transform the exported translations by adding plugins. A plugin must
inherit from I18nJS::Plugin and can override 4 instance methods (they're all
optional and default to a noop implementation). For real examples, see
lib/i18n-js/embed_fallback_translations_plugin.rb
and
lib/i18n-js/export_files_plugin.rb
# frozen_string_literal: true
module I18nJS
class SamplePlugin < I18nJS::Plugin
# This method is responsible for transforming the translations. The
# translations you'll receive may already be filtered by other plugins
# and by the default filtering itself. If you need to access the original
# translations, use `I18nJS.translations`.
def transform(translations:)
# transform `translations` here…
translations
end
# In case your plugin accepts configuration, this is where you must validate
# it, making sure only valid keys and types are provided.
# If the configuration contains invalid data, raise an exception using
# `raise I18nJS::Schema::InvalidError, error_message`.
# Paths passed to schema helpers are relative to the plugin's own config
# root (i.e. the pipeline stage object, minus the `plugin:` key).
def validate_schema
# validate plugin schema here…
end
# Called after schema validation. Use this for any one-time plugin setup.
def setup
end
# This method is called whenever `I18nJS.call(**kwargs)` finishes exporting
# JSON files based on your configuration.
#
# You can use it to further process exported files, or generate new files
# based on the translations that have been exported.
def after_export(files:)
# process exported files here…
end
end
end
The class I18nJS::Plugin exposes the following helpers:
I18nJS::Plugin.key: the configuration key inferred from your plugin's class
name (e.g. SamplePlugin → "sample"). Override this class method to use a
custom key.I18nJS::Plugin#config: the plugin's own configuration hash (the pipeline
stage entry, minus the plugin: key).I18nJS::Plugin#main_config: the full i18n-js configuration hash.I18nJS::Plugin#enabled?: whether the plugin is enabled based on
config[:enabled].Plugins are configured via the pipeline: array in the config file. Each entry
must have a plugin: key matching Plugin.key and an enabled: boolean. Any
additional keys in the stage are passed to the plugin as part of config.
pipeline:
- plugin: sample
enabled: true
my_option: value
To distribute a plugin, create a gem whose load path contains a file matching
the pattern i18n-js/*_plugin.rb. You can verify it will be found by running
Gem.find_files("i18n-js/*_plugin.rb") in an IRB session after installing your
gem.
To list missing and extraneous translations, you can use
i18n lint:translations. This command will load your translations similarly to
how i18n export does, but will output the list of keys that don't have a
matching translation against the default locale. Here's an example:
$ i18n lint:translations
=> Config file: "./config/i18n.yml"
=> Require file: "./config/environment.rb"
=> Check "./config/i18n.yml" for ignored keys.
=> en: 232 translations
=> pt-BR: 5 missing, 1 extraneous, 1 ignored
- pt-BR.actors.github.metrics (missing)
- pt-BR.actors.github.metrics_hint (missing)
- pt-BR.actors.github.repo_metrics (missing)
- pt-BR.actors.github.repository (missing)
- pt-BR.actors.github.user_metrics (missing)
- pt-BR.github.repository (extraneous)
This command will exit(missing_count) whenever there are missing translations.
This way you can use it as a CI linting tool.
You can ignore keys by adding a list to the config file:
---
translations:
- file: app/frontend/locales/en.json
patterns:
- "*"
- "!*.activerecord.*"
- "!*.errors.*"
- "!*.number.nth.*"
- file: app/frontend/locales/:locale.:digest.json
patterns:
- "*"
lint_translations:
ignore:
- "*.mailer.login.subject"
- "*.mailer.login.body"
To lint your script files and check for missing translations (which can signal
that you're either using wrong scopes or forgot to add the translation), use
i18n lint:scripts. This command will parse your JavaScript/TypeScript files
and extract all scopes being used. This command requires a Node.js runtime. You
can either specify one via --node-path, or let the plugin infer a binary from
your $PATH.
The comparison will be made against the export JSON files, which means it'll consider transformations performed by plugins (e.g. the output files may be affected by