README.md in i18n-js-4.1.0 vs README.md in i18n-js-4.2.0
- old
+ new
@@ -138,95 +138,130 @@
```yaml
embed_fallback_translations:
enabled: true
```
+##### `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:
+
+```yaml
+export_files:
+ enabled: true
+ files:
+ - template: path/to/template.erb
+ output: "%{dir}/%{base_name}.ts"
+```
+
+You can export multiple files by define 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:
+
+```erb
+/* 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:
+
+```typescript
+/* 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!",
+ },
+});
+```
+
#### Plugin API
You can transform the exported translations by adding plugins. A plugin must
-inherit from `I18nJS::Plugin` and can have 3 class methods. The following
-example shows how the built-in `embed_fallback_translations` plugin is
-implemented.
+inherit from `I18nJS::Plugin` and can have 4 class methods. To see a real
+example, see
+[lib/i18n-js/embed_fallback_translations_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/embed_fallback_translations_plugin.rb)
+Here's the base `I18nJS::Plugin` class with the documented api:
+
```ruby
# frozen_string_literal: true
module I18nJS
- require "i18n-js/plugin"
-
- class EmbedFallbackTranslationsPlugin < I18nJS::Plugin
- CONFIG_KEY = :embed_fallback_translations
-
- # This method must set up the basic plugin configuration, like adding the
- # config's root key in case your plugin accepts configuration (defined via
- # the config file).
+ class Plugin
+ # This method is responsible for transforming the translations. The
+ # translations you'll receive may be already be filtered by other plugins
+ # and by the default filtering itself. If you need to access the original
+ # translations, use `I18nJS.translations`.
#
- # If you don't add this key, the linter will prevent non-default keys from
- # being added to the configuration file.
- def self.setup
- I18nJS::Schema.root_keys << CONFIG_KEY
+ # Make sure you always check whether your plugin is active before
+ # transforming translations; otherwise, opting out transformation won't be
+ # possible.
+ def self.transform(translations:, config:)
+ translations
end
# In case your plugin accepts configuration, this is where you must validate
# the configuration, making sure only valid keys and type is provided.
# If the configuration contains invalid data, then you must raise an
# exception using something like
# `raise I18nJS::Schema::InvalidError, error_message`.
def self.validate_schema(config:)
- return unless config.key?(CONFIG_KEY)
+ end
- plugin_config = config[CONFIG_KEY]
- valid_keys = %i[enabled]
- schema = I18nJS::Schema.new(config)
-
- schema.expect_required_keys(valid_keys, plugin_config)
- schema.reject_extraneous_keys(valid_keys, plugin_config)
- schema.expect_enabled_config(CONFIG_KEY, plugin_config[:enabled])
+ # This method must set up the basic plugin configuration, like adding the
+ # config's root key in case your plugin accepts configuration (defined via
+ # the config file).
+ #
+ # If you don't add this key, the linter will prevent non-default keys from
+ # being added to the configuration file.
+ def self.setup
end
- # This method is responsible for transforming the translations. The
- # translations you'll receive may be already be filtered by other plugins
- # and by the default filtering itself. If you need to access the original
- # translations, use `I18nJS.translations`.
+ # 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.
+ #
# Make sure you always check whether your plugin is active before
- # transforming translations; otherwise, opting out transformation won't be
- # possible.
- def self.transform(translations:, config:)
- return translations unless config.dig(CONFIG_KEY, :enabled)
-
- translations_glob = Glob.new(translations)
- translations_glob << "*"
-
- mapping = translations.keys.each_with_object({}) do |locale, buffer|
- buffer[locale] = Glob.new(translations[locale]).tap do |glob|
- glob << "*"
- end
- end
-
- default_locale = I18n.default_locale
- default_locale_glob = mapping.delete(default_locale)
- default_locale_paths = default_locale_glob.paths
-
- mapping.each do |locale, glob|
- missing_keys = default_locale_paths - glob.paths
-
- missing_keys.each do |key|
- components = key.split(".").map(&:to_sym)
- fallback_translation = translations.dig(default_locale, *components)
-
- next unless fallback_translation
-
- translations_glob.set([locale, key].join("."), fallback_translation)
- end
- end
-
- translations_glob.to_h
+ # processing files; otherwise, opting out won't be possible.
+ def self.after_export(files:, config:)
end
end
-
- I18nJS.register_plugin(EmbedFallbackTranslationsPlugin)
end
```
To distribute this plugin, you need to create a gem package that matches the
pattern `i18n-js/*_plugin.rb`. You can test whether your plugin will be found by
@@ -240,11 +275,24 @@
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` command in action](https://github.com/fnando/i18n-js/raw/main/images/i18njs-check.gif)
+```console
+$ 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 exist with status 1 whenever there are missing translations.
This way you can use it as a CI linting.
You can ignore keys by adding a list to the config file:
@@ -298,12 +346,12 @@
Notice that only literal strings can be used, as in `i18n.t("message")`. If
you're using dynamic scoping through variables (e.g.
`const scope = "message"; i18n.t(scope)`), they will be skipped.
```console
-$ i18n lint:scripts --config test/config/lint.yml --require test/config/require.rb
-=> Config file: "test/config/lint.yml"
-=> Require file: "test/config/require.rb"
+$ i18n lint:scripts
+=> Config file: "./config/i18n.yml"
+=> Require file: "./config/environment.rb"
=> Node: "/Users/fnando/.asdf/shims/node"
=> Available locales: [:en, :es, :pt]
=> Patterns: ["!(node_modules)/**/*.js", "!(node_modules)/**/*.ts", "!(node_modules)/**/*.jsx", "!(node_modules)/**/*.tsx"]
=> 9 translations, 11 missing, 4 ignored
- test/scripts/lint/file.js:1:1: en.js.missing