README.md in i18n-js-3.9.2 vs README.md in i18n-js-4.0.0.alpha1

- old
+ new

@@ -1,1099 +1,142 @@ -<p align="center"> - <img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/i18njs.png" alt="i18n.js"> -</p> +# i18n-js -<p align="center"> - It's a small library to provide the Rails I18n translations on the JavaScript. -</p> +[![Tests](https://github.com/fnando/i18n-js/workflows/ruby-tests/badge.svg)](https://github.com/fnando/i18n-js) +[![Code Climate](https://codeclimate.com/github/fnando/i18n-js/badges/gpa.svg)](https://codeclimate.com/github/fnando/i18n-js) +[![Gem](https://img.shields.io/gem/v/i18n-js.svg)](https://rubygems.org/gems/i18n-js) +[![Gem](https://img.shields.io/gem/dt/i18n-js.svg)](https://rubygems.org/gems/i18n-js) -<p align="center"> - <a href="https://github.com/fnando/i18n-js/actions?query=workflow%3ATests"><img src="https://github.com/fnando/i18n-js/workflows/Tests/badge.svg" alt="Tests"></a> - <a href="https://badge.fury.io/rb/i18n-js"><img src="https://img.shields.io/gem/v/i18n-js.svg" alt="Gem Version"></a> - <a href="https://www.npmjs.com/package/i18n-js"><img src="https://img.shields.io/npm/v/i18n-js.svg" alt="npm"></a> - <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a> - <a href="https://coveralls.io/r/fnando/i18n-js"><img src="https://img.shields.io/coveralls/fnando/i18n-js.svg" alt="Coverage Status"></a> - <a href="https://gitter.im/fnando/i18n-js"><img src="https://img.shields.io/badge/gitter-join%20chat-1dce73.svg" alt="Gitter"></a> -</p> +Export [i18n](https://rubygems.org/gems/i18n) translations to JSON. A perfect +fit if you want to export translations to JavaScript. ---- +Oh, you don't use Ruby? No problem! You can still use i18n-js and the +[companion JavaScript package](https://npmjs.com/package/i18n-js). -Features: +## Installation -- Pluralization -- Date/Time localization -- Number localization -- Locale fallback -- Asset pipeline support -- Lots more! :) +```bash +gem install i18n-js +``` -## Version Notice +Or add the following line to your project's Gemfile: -The `main` branch (including this README) is for latest `3.0.0` instead of -`2.x`. - -## Usage - -### Installation - -#### Rails app - -Add the gem to your Gemfile. - ```ruby -gem "i18n-js" +gem "i18n-js", "~> 4.0.0.alpha1" ``` -#### Rails with [webpacker](https://github.com/rails/webpacker) +## Usage -If you're using `webpacker`, you may need to add the dependencies to your client -with: +About patterns: -``` -yarn add i18n-js -# or, if you're using npm, -npm install i18n-js -``` +- Patterns can use `*` as a wildcard and can appear more than once. + - `*` will include everything + - `*.messages.*` +- Patterns starting with `!` are excluded. + - `!*.activerecord.*` will exclude all ActiveRecord translations. -For more details, see: -- [this gist](https://gist.github.com/bazzel/ecdff4718962e57c2d5569cf01d332fe) -- https://github.com/fnando/i18n-js/issues/597 +The config file: -#### Rails app with [Asset Pipeline](https://guides.rubyonrails.org/asset_pipeline.html) - -If you're using the -[asset pipeline](https://guides.rubyonrails.org/asset_pipeline.html), then you -must add the following line to your `app/assets/javascripts/application.js`. - -```javascript -// -// This is optional (in case you have `I18n is not defined` error) -// If you want to put this line, you must put it BEFORE `i18n/translations` -//= require i18n -// Some people even need to add the extension to make it work, see https://github.com/fnando/i18n-js/issues/283 -//= require i18n.js -// -// This is a must -//= require i18n/translations -``` - -#### Rails app without [Asset Pipeline](https://guides.rubyonrails.org/asset_pipeline.html) - -First, put this in your `application.html` (layout file). Then get the JS files -following the instructions below. - -```erb -<%# This is just an example, you can put `i18n.js` and `translations.js` anywhere you like %> -<%# Unlike the Asset Pipeline example, you need to require both **in order** %> -<%= javascript_include_tag "i18n" %> -<%= javascript_include_tag "translations", skip_pipeline: true %> -``` - -**There are two ways to get `translations.js` (For Rails app without Asset -Pipeline).** - -1. This `translations.js` file can be automatically generated by the - `I18n::JS::Middleware`. Just add `config.middleware.use I18n::JS::Middleware` - to your `config/application.rb` file. -2. If you can't or prefer not to generate this file, you can move the middleware - line to your `config/environments/development.rb` file and run - `rake i18n:js:export` before deploying. This will export all translation - files, including the custom scopes you may have defined on - `config/i18n-js.yml`. If `I18n.available_locales` is set (e.g. in your Rails - `config/application.rb` file) then only the specified locales will be - exported. Current version of `i18n.js` will also be exported to avoid version - mismatching by downloading. - -#### Export Configuration (For translations) - -Exported translation files generated by `I18n::JS::Middleware` or -`rake i18n:js:export` can be customized with config file `config/i18n-js.yml` -(use `rails generate i18n:js:config` to create it). You can even get more files -generated to different folders and with different translations to best suit your -needs. The config file also affects developers using Asset Pipeline to require -translations. Except the option `file`, since all translations are required by -adding `//= require i18n/translations`. - -Examples: - -```yaml +```yml +--- translations: - - file: "public/javascripts/path-to-your-messages-file.js" - only: "*.date.formats" - - file: "public/javascripts/path-to-your-second-file.js" - only: ["*.activerecord", "*.admin.*.title"] -``` + - file: app/frontend/locales/en.json + patterns: + - "*" + - "!*.activerecord" + - "!*.errors" + - "!*.number.nth" -If `only` is omitted all the translations will be saved. Also, make sure you add -that initial `*`; it specifies that all languages will be exported. If you want -to export only one language, you can do something like this: - -```yaml -translations: - - file: "public/javascripts/en.js" - only: "en.*" - - file: "public/javascripts/pt-BR.js" - only: "pt-BR.*" + - file: app/frontend/locales/:locale.json + patterns: + - "*" ``` -Optionally, you can auto generate a translation file per available locale if you -specify the `%{locale}` placeholder. +The Ruby API: -```yaml -translations: - - file: "public/javascripts/i18n/%{locale}.js" - only: "*" - - file: "public/javascripts/frontend/i18n/%{locale}.js" - only: ["*.frontend", "*.users.*"] -``` - -You can also include ERB in your config file. - -```yaml -translations: -<% Widgets.each do |widget| %> -- file: <%= "'#{widget.file}'" %> - only: <%= "'#{widget.only}'" %> -<% end %> -``` - -You are able to exclude certain phrases or whole groups of phrases by specifying -the YAML key(s) in the `except` configuration option. The outputted JS -translations file (exported or generated by the middleware) will omit any keys -listed in `except` configuration param: - -```yaml -translations: - - except: ["*.active_admin", "*.ransack", "*.activerecord.errors"] -``` - -#### Export Configuration (For other things) - -- `I18n::JS.config_file_path` Expected Type: `String` Default: - `config/i18n-js.yml` Behaviour: Try to read the config file from that location - -- `I18n::JS.export_i18n_js_dir_path` Expected Type: `String` Default: - `public/javascripts` Behaviour: - - - Any `String`: considered as a relative path for a folder to `Rails.root` and - export `i18n.js` to that folder for `rake i18n:js:export` - - Any non-`String` (`nil`, `false`, `:none`, etc): Disable `i18n.js` exporting - -- `I18n::JS.sort_translation_keys` Expected Type: `Boolean` Default: `true` - Behaviour: - - - Sets whether or not to deep sort all translation keys in order to generate - identical output for the same translations - - Set to true to ensure identical asset fingerprints for the asset pipeline - -- You may also set `export_i18n_js` and `sort_translation_keys` in your config - file, e.g.: - -```yaml -export_i18n_js: false -# OR -export_i18n_js: "my/path" - -sort_translation_keys: false - -translations: - - ... -``` - -To find more examples on how to use the configuration file please refer to the -tests. - -#### Fallbacks - -If you specify the `fallbacks` option, you will be able to fill missing -translations with those inside fallback locale(s). Default value is `true`. - -Examples: - -```yaml -fallbacks: true - -translations: - - file: "public/javascripts/i18n/%{locale}.js" - only: "*" -``` - -This will enable merging fallbacks into each file. (set to `false` to disable). -If you use `I18n` with fallbacks, the fallbacks defined there will be used. -Otherwise `I18n.default_locale` will be used. - -```yaml -fallbacks: :de - -translations: - - file: "public/javascripts/i18n/%{locale}.js" - only: "*" -``` - -Here, the specified locale `:de` will be used as fallback for all locales. - -```yaml -fallbacks: - fr: ["de", "en"] - de: "en" - -translations: - - file: "public/javascripts/i18n/%{locale}.js" - only: "*" -``` - -Fallbacks defined will be used, if not defined (e.g. `:pl`) `I18n.fallbacks` or -`I18n.default_locale` will be used. - -```yaml -fallbacks: :default_locale - -translations: - - file: "public/javascripts/i18n/%{locale}.js" - only: "*" -``` - -Setting the option to `:default_locale` will enforce the fallback to use the -`I18n.default_locale`, ignoring `I18n.fallbacks`. - -Examples: - -```yaml -fallbacks: false - -translations: - - file: "public/javascripts/i18n/%{locale}.js" - only: "*" -``` - -You must disable this feature by setting the option to `false`. - -To find more examples on how to use the configuration file please refer to the -tests. - -#### Available locales - -By specifying option `js_available_locales` with a list of locales, this list -would be used instead of default `I18n.available_locales` to generate translations. - -Example: - -```yaml -js_available_locales: ["de", "en"] -``` - -#### Namespace - -Setting the `namespace` option will change the namespace of the output -Javascript file to something other than `I18n`. This can be useful in -no-conflict scenarios. Example: - -```yaml -translations: - - file: "public/javascripts/i18n/translations.js" - namespace: "MyNamespace" -``` - -will create: - -``` -MyNamespace.translations || (MyNamespace.translations = {}); -MyNamespace.translations["en"] = { ... } -``` - -### Adding prefix & suffix to the translations file(s) - -Setting the `prefix: "import I18n from 'i18n-js';\n"` option will add the line -at the beginning of the resultant translation file. This can be useful to use -this gem with the [i18n-js](https://www.npmjs.com/package/i18n-js) npm package, -which is quite useful to use it with webpack. The user should provide the -semi-colon and the newline character if needed. - -For example: - -```yaml -translations: - - file: "public/javascripts/i18n/translations.js" - prefix: "import I18n from 'i18n-js';\n" -``` - -will create: - -``` -import I18n from 'i18n-js'; -I18n.translations || (I18n.translations = {}); -``` - -`suffix` option is added in https://github.com/fnando/i18n-js/pull/561. -It's similar to `prefix` so won't explain it in details. - -#### Pretty Print - -Set the `pretty_print` option if you would like whitespace and indentation in -your output file (default: false) - -```yaml -translations: - - file: "public/javascripts/i18n/translations.js" - pretty_print: true -``` - -#### Javascript Deep Merge (:js_extend option) - -By default, the output file Javascript will call the `I18n.extend` method to -ensure that newly loaded locale files are deep-merged with any locale data -already in memory. To disable this either globally or per-file, set the -`js_extend` option to false - -```yaml -js_extend: false # this will disable Javascript I18n.extend globally -translations: - - file: "public/javascripts/i18n/translations.js" - js_extend: false # this will disable Javascript I18n.extend for this file -``` - -#### Vanilla JavaScript - -Just add the `i18n.js` file to your page. You'll have to build the translations -object by hand or using your favorite programming language. More info below. - -#### Via NPM with webpack and CommonJS - -Add the following line to your package.json dependencies where version is the -version you want: - -```javascript -"i18n-js": "{version_constraint}" - -// Or if you want unreleased version -// npm install requires it to be the gzipped tarball, see [npm install](https://www.npmjs.org/doc/cli/npm-install.html) -"i18n-js": "https://github.com/fnando/i18n-js/archive/{tag_name_or_branch_name_or_commit_sha}.tar.gz" -``` - -Run npm install then use via - -```javascript -var i18n = require("i18n-js"); -``` - -### Setting up - -You **don't** need to set up a thing. The default settings will work just okay. -But if you want to split translations into several files or specify contexts, -you can follow the rest of this setting up section. - -Set your locale is easy as - -```javascript -I18n.defaultLocale = "pt-BR"; -I18n.locale = "pt-BR"; -I18n.currentLocale(); -// pt-BR -``` - -**NOTE:** You can now apply your configuration **before I18n** is loaded like -this: - -```javascript -I18n = {}; // You must define this object in top namespace, which should be `window` -I18n.defaultLocale = "pt-BR"; -I18n.locale = "pt-BR"; - -// Load I18n from `i18n.js`, `application.js` or whatever - -I18n.currentLocale(); -// pt-BR -``` - -In practice, you'll have something like the following in your -`application.html.erb`: - -```erb -<script type="text/javascript"> - I18n.defaultLocale = "<%= I18n.default_locale %>"; - I18n.locale = "<%= I18n.locale %>"; -</script> -``` - -You can use translate your messages: - -```javascript -I18n.t("some.scoped.translation"); -// or translate with explicit setting of locale -I18n.t("some.scoped.translation", { locale: "fr" }); -``` - -You can also interpolate values: - -```javascript -// You need the `translations` object setup first -I18n.translations["en"] = { - greeting: "Hello %{name}", -}; - -I18n.t("greeting", { name: "John Doe" }); -``` - -You can set default values for missing scopes: - -```javascript -// simple translation -I18n.t("some.missing.scope", { defaultValue: "A default message" }); - -// with interpolation -I18n.t("noun", { defaultValue: "I'm a {{noun}}", noun: "Mac" }); -``` - -You can also provide a list of default fallbacks for missing scopes: - -```javascript -// As a scope -I18n.t("some.missing.scope", { defaults: [{ scope: "some.existing.scope" }] }); - -// As a simple translation -I18n.t("some.missing.scope", { defaults: [{ message: "Some message" }] }); -``` - -Default values must be provided as an array of hashes where the key is the type -of translation desired, a `scope` or a `message`. The translation returned will -be either the first scope recognized, or the first message defined. - -The translation will fallback to the `defaultValue` translation if no scope in -`defaults` matches and if no default of type `message` is found. - -Translation fallback can be enabled by enabling the `I18n.fallbacks` option: - -```erb -<script type="text/javascript"> - I18n.fallbacks = true; -</script> -``` - -By default missing translations will first be looked for in less specific -versions of the requested locale and if that fails by taking them from your -`I18n.defaultLocale`. - -```javascript -// if I18n.defaultLocale = "en" and translation doesn't exist -// for I18n.locale = "de-DE" this key will be taken from "de" locale scope -// or, if that also doesn't exist, from "en" locale scope -I18n.t("some.missing.scope"); -``` - -Custom fallback rules can also be specified for a particular language. There are -three different ways of doing it so: - -```javascript -I18n.locales.no = ["nb", "en"]; -I18n.locales.no = "nb"; -I18n.locales.no = function (locale) { - return ["nb"]; -}; -``` - -### Translation Missing Behaviour Control - -By default a missing translation will be displayed as - - [missing "name of scope" translation] - -While you are developing or if you do not want to provide a translation in the -default language you can set - -```javascript -I18n.missingBehaviour = "guess"; -``` - -this will take the last section of your scope and guess the intended value. -Camel case becomes lower cased text and underscores are replaced with space - - questionnaire.whatIsYourFavorite_ChristmasPresent - -becomes "what is your favorite Christmas present" - -#### Option `missingTranslationPrefix` - -In order to still detect untranslated strings, you can set -`I18n.missingTranslationPrefix` to something like: - -```javascript -I18n.missingTranslationPrefix = "EE: "; -``` - -And result will be: - -```javascript -"EE: what is your favorite Christmas present"; - -``` - -This will help you doing automated tests against your localisation assets. - -#### Customize return when translation entry missing - -Some people prefer returning `null`/`undefined` for missing translation: - -```javascript -I18n.missingTranslation = function (scope, options) { - return undefined; -}; -``` - -### Option `defaultSeparator` (global) / `separator` (local) - -Default separator of translation key is `.` (dot) -Meaning `I18n.t("scope.entry")` would search for translation entry `I18n.translations[locale].scope.entry` -Using a different separator can be done either globally or locally. - -Globally: `I18n.defaultSeparator = newSeparator` -Locally: `I18n.t("full_sentences|Server Busy. Please retry later", {separator: '|'})` - -### Pluralization - -Pluralization is possible as well and by default provides English rules: - -```javascript -I18n.t("inbox.counting", { count: 10 }); // You have 10 messages -``` - -The sample above expects the following translation: - -```yaml -en: - inbox: - counting: - one: You have 1 new message - other: You have {{count}} new messages - zero: You have no messages -``` - -**NOTE:** Rails I18n recognizes the `zero` option. - -If you need special rules just define them for your language, for example -Russian, just add a new pluralizer: - -```javascript -I18n.pluralization["ru"] = function (count) { - var key = - count % 10 == 1 && count % 100 != 11 - ? "one" - : [2, 3, 4].indexOf(count % 10) >= 0 && - [12, 13, 14].indexOf(count % 100) < 0 - ? "few" - : count % 10 == 0 || - [5, 6, 7, 8, 9].indexOf(count % 10) >= 0 || - [11, 12, 13, 14].indexOf(count % 100) >= 0 - ? "many" - : "other"; - return [key]; -}; -``` - -You can find all rules on -<https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html>. - -If you're using the same scope over and over again, you may use the `scope` -option. - -```javascript -var options = { scope: "activerecord.attributes.user" }; - -I18n.t("name", options); -I18n.t("email", options); -I18n.t("username", options); -``` - -You can also provide an array as scope. - -```javascript -// use the greetings.hello scope -I18n.t(["greetings", "hello"]); -``` - -#### Number formatting - -Similar to Rails helpers, you have localized number and currency formatting. - -```javascript -I18n.l("currency", 1990.99); -// $1,990.99 - -I18n.l("number", 1990.99); -// 1,990.99 - -I18n.l("percentage", 123.45); -// 123.450% -``` - -To have more control over number formatting, you can use the `I18n.toNumber`, -`I18n.toPercentage`, `I18n.toCurrency` and `I18n.toHumanSize` functions. - -```javascript -I18n.toNumber(1000); // 1,000.000 -I18n.toCurrency(1000); // $1,000.00 -I18n.toPercentage(100); // 100.000% -``` - -The `toNumber` and `toPercentage` functions accept the following options: - -- `precision`: defaults to `3` -- `separator`: defaults to `.` -- `delimiter`: defaults to `,` -- `strip_insignificant_zeros`: defaults to `false` - -See some number formatting examples: - -```javascript -I18n.toNumber(1000, { precision: 0 }); // 1,000 -I18n.toNumber(1000, { delimiter: ".", separator: "," }); // 1.000,000 -I18n.toNumber(1000, { delimiter: ".", precision: 0 }); // 1.000 -``` - -The `toCurrency` function accepts the following options: - -- `precision`: sets the level of precision -- `separator`: sets the separator between the units -- `delimiter`: sets the thousands delimiter -- `format`: sets the format of the output string -- `unit`: sets the denomination of the currency -- `strip_insignificant_zeros`: defaults to `false` -- `sign_first`: defaults to `true` - -You can provide only the options you want to override: - -```javascript -I18n.toCurrency(1000, { precision: 0 }); // $1,000 -``` - -The `toHumanSize` function accepts the following options: - -- `precision`: defaults to `1` -- `separator`: defaults to `.` -- `delimiter`: defaults to `""` -- `strip_insignificant_zeros`: defaults to `false` -- `format`: defaults to `%n%u` -- `scope`: defaults to `""` - -<!----> - -```javascript -I18n.toHumanSize(1234); // 1KB -I18n.toHumanSize(1234 * 1024); // 1MB -``` - -#### Date formatting - -```javascript -// accepted formats -I18n.l("date.formats.short", "2009-09-18"); // yyyy-mm-dd -I18n.l("time.formats.short", "2009-09-18 23:12:43"); // yyyy-mm-dd hh:mm:ss -I18n.l("time.formats.short", "2009-11-09T18:10:34"); // JSON format with local Timezone (part of ISO-8601) -I18n.l("time.formats.short", "2009-11-09T18:10:34Z"); // JSON format in UTC (part of ISO-8601) -I18n.l("date.formats.short", 1251862029000); // Epoch time -I18n.l("date.formats.short", "09/18/2009"); // mm/dd/yyyy -I18n.l("date.formats.short", new Date()); // Date object -``` - -You can also add placeholders to the date format: - -```javascript -I18n.translations["en"] = { - date: { - formats: { - ordinal_day: "%B %{day}", - }, - }, -}; - -I18n.l("date.formats.ordinal_day", "2009-09-18", { day: "18th" }); // Sep 18th -``` - -If you prefer, you can use the `I18n.toTime` and `I18n.strftime` functions to -format dates. - -```javascript -var date = new Date(); -I18n.toTime("date.formats.short", "2009-09-18"); -I18n.toTime("date.formats.short", date); -I18n.strftime(date, "%d/%m/%Y"); -``` - -The accepted formats for `I18n.strftime` are: - - %a - The abbreviated weekday name (Sun) - %A - The full weekday name (Sunday) - %b - The abbreviated month name (Jan) - %B - The full month name (January) - %c - The preferred local date and time representation - %d - Day of the month (01..31) - %-d - Day of the month (1..31) - %H - Hour of the day, 24-hour clock (00..23) - %-H/%k - Hour of the day, 24-hour clock (0..23) - %I - Hour of the day, 12-hour clock (01..12) - %-I/%l - Hour of the day, 12-hour clock (1..12) - %m - Month of the year (01..12) - %-m - Month of the year (1..12) - %M - Minute of the hour (00..59) - %-M - Minute of the hour (0..59) - %p - Meridian indicator (AM or PM) - %P - Meridian indicator (am or pm) - %S - Second of the minute (00..60) - %-S - Second of the minute (0..60) - %w - Day of the week (Sunday is 0, 0..6) - %y - Year without a century (00..99) - %-y - Year without a century (0..99) - %Y - Year with century - %z/%Z - Timezone offset (+0545) - -Check out `spec/*.spec.js` files for more examples! - -#### Using pluralization and number formatting together - -Sometimes you might want to display translation with formatted number, like -adding thousand delimiters to displayed number You can do this: - -```json -{ - "en": { - "point": { - "one": "1 Point", - "other": "{{formatted_number}} Points", - "zero": "0 Points" - } - } -} -``` - -```js -var point_in_number = 1000; -I18n.t("point", { - count: point_in_number, - formatted_number: I18n.toNumber(point_in_number), -}); -``` - -Output should be `1,000 points` - -## Using multiple exported translation files on a page. - -This method is useful for very large apps where a single contained -translations.js file is not desirable. Examples would be a global translations -file and a more specific route translation file. - -### Rails without asset pipeline - -1. Setup your `config/i18n-js.yml` to have multiple files and try to minimize - any overlap. - -```yaml -sort_translation_keys: true -fallbacks: false - -translations: - + file: "app/assets/javascript/nls/welcome.js" - only: - + '*.welcome.*' - - + file: "app/assets/javascript/nls/albums.js" - only: - + '*.albums.*' - - + file: "app/assets/javascript/nls/global.js" - only: - + '*' - # Exempt any routes specific translations from being - # included in the global translation file - except: - + '*.welcome.*' - + '*.albums.*' -``` - -When `rake i18n:js:export` is executed it will create 3 translations files that -can be loaded via the `javascript_include_tag` - -2. Add the `javascript_include_tag` to your layout and to any route specific - files that will require it. - ```ruby - # views/layouts/application.html.erb - <%= javascript_include_tag( - "i18n" - "nls/global" - ) %> -``` +require "i18n-js" -and in the route specific - -```ruby - # views/welcome/index.html.erb - <%= javascript_include_tag( - "nls/welcome" - ) %> +I18nJS.call(config_file: "config/i18n.yml") +I18nJS.call(config: config) ``` -3. Make sure that you add these files to your `config/application.rb` +The CLI API: -```ruby - config.assets.precompile += %w( - i18n - nls/* - ) +```console +$ i18n init --config config/i18n.yml +$ i18n export --config config/i18n.yml --require config/environment.rb ``` -### Using require.js / r.js +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`. -To use this with require.js we are only going to change a few things from above. +## Automatically export translations -1. In your `config/i18n-js.yml` we need to add a better location for the i18n to - be exported to. You want to use this location so that it can be properly - precompiled by r.js. +### Using guard -```yaml -export_i18n_js: "app/assets/javascript/nls" -``` +Install [guard](https://rubygems.org/packages/guard) and +[guard-compat](https://rubygems.org/packages/guard-compat). Then create a +Guardfile with the following configuration: -2. In your `config/require.yml` we need to add a map, shim all the translations, - and include them into the appropriate modules - -```yaml -# In your maps add (if you do not have this you will need to add it) -map: - '*': - i18n: 'nls/i18n' - -# In your shims -shims: - nls/welcome: - deps: - + i18n - - nls/global: - deps: - + i18n - -# Finally in your modules -modules: - + name: 'application' - include: - + i18n - + 'nls/global' - - + name: 'welcome' - exclude: - + application - include: - + 'nls/welcome' -``` - -3. When `rake assets:precompile` is executed it will optimize the translations - into the correct modules so they are loaded with their assigned module, and - loading them with requirejs is as simple as requiring any other shim. - -```javascript -define(["welcome/other_asset", "nls/welcome"], function (otherAsset) { - // ... -}); -``` - -4. (optional) As an additional configuration we can make a task to be run before - the requirejs optimizer. This will allow any automated scripts that run the - requirejs optimizer to export the strings before we run r.js. - -```rake -# lib/tasks/i18n.rake -Rake::Task[:'i18n:js:export'].prerequisites.clear -task :'i18n:js:export' => :'i18n:js:before_export' -task :'requirejs:precompile:external' => :'i18n:js:export' - -namespace :i18n do - namespace :js do - task :before_export => :'assets:environment' do - I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{yml,rb}')] - I18n.backend.load_translations - end - end +```ruby +guard(:"i18n-js", + run_on_start: true, + config_file: "./config/i18n.yml", + require_file: "./config/environment.rb") do + watch(%r{^(app|config)/locales/.+\.(yml|po)$}) + watch(%r{^config/i18n.yml$}) + watch("Gemfile") end ``` -## Using I18n.js with other languages (Python, PHP, ...) +If you files in a different location, the remember to configure file paths +accordingly. -The JavaScript library is language agnostic; so you can use it with PHP, Python, -[your favorite language here]. The only requirement is that you need to set the -`translations` attribute like following: +Now you can run `guard start -i`. -```javascript -I18n.translations = {}; +### Using listen -I18n.translations["en"] = { - message: "Some special message for you", -}; +Create a file under `config/initializers/i18n.rb` with the following content: -I18n.translations["pt-BR"] = { - message: "Uma mensagem especial para vocĂȘ", -}; -``` - -## Known Issues - -### Missing translations in precompiled file(s) after adding any new locale file - -Due to the design of `sprockets`: - -- `depend_on` only takes file paths, not directory paths -- registered `preprocessors` are only run when the fingerprint of any asset - file, including `.erb` files, is changed - -This means that new locale files will not be detected, and so they will not -trigger a i18n-js refresh. There are a few approaches to work around this: - -1. You can force i18n-js to update its translations by completely clearing the - assets cache. Use one of the following: - -```bash -$ rake assets:clobber -# Or, with older versions of Rails: -$ rake tmp:cache:clear -``` - -These commands will remove _all_ fingerprinted assets, and you will have to -recompile them with - -```bash -$ rake assets:precompile -``` - -or similar commands. If you are precompiling assets on the target machine(s), -cached pages may be broken by this, so they will need to be refreshed. - -2. You can change something in a different locale file. - -3. Finally, you can change `config.assets.version`. - -**Note:** See issue [#213](https://github.com/fnando/i18n-js/issues/213) for -more details and discussion of this issue. - -### Translations in JS are not updated when Sprockets not loaded before this gem - -The "rails engine" declaration will try to detect existence of "sprockets" -before adding the initailizer If sprockets is loaded after this gem, the -preprocessor for making JS translations file cache to depend on content of -locale files will not be hooked. So ensure sprockets is loaded before this gem -by moving the entry of sprockets in the Gemfile or adding "require" statements -for sprockets somewhere. - -**Note:** See issue [#404](https://github.com/fnando/i18n-js/issues/404) for -more details and discussion of this issue. - -### JS `I18n.toCurrency` & `I18n.toNumber` cannot handle large integers - -The above methods use `toFixed` and it only supports 53 bit integers. Ref: -https://2ality.com/2012/07/large-integers.html - -Feel free to find & discuss possible solution(s) at issue -[#511](https://github.com/fnando/i18n-js/issues/511) - -### May not work with all backend implementations - -I18n backend implementations have to conform to a specific interface to work -with i18n-js. For backends that do not conform to the interface, you will likely -get an exception like this: - -``` -Undefined method 'initialized?' for <your backend class> -``` - -For now, i18n-js is compatible with the `Simple` backend and with -`I18n::Backend::ActiveRecord` (>= 0.4.0). - -If you need a more sophisticated backend for your rails application that doesn't -implement the required methods, you can setup i18n-js to get translations from a -separate `Simple` backend, by adding the following in an initializer: - ```ruby -I18n::JS.backend = I18n.backend -I18n.backend = I18n::Backend::Chain.new(<your other backend(s)>, I18n.backend) -``` +# frozen_string_literal: true -This will use your backend with the default `Simple` backend as fallback, while -i18n-js only sees and uses the simple backend. This means however, that only -translations from your static locale files will be present in JavaScript. +require "i18n-js/listen" -If you do cannot use a `Chain`-Backend for some reason, you can also set - -```ruby -I18n::JS.backend = I18n::Backend::Simple.new -I18n.backend = <something different> +I18nJS.listen ``` -However, the automatic reloading of translations in developement will not work -in this case. This is because Rails calls `I18n.reload!` for each request in -development, but `reload!` will not be called on `I18n::JS.backend`, since it is -a different object. One option would be to patch `I18n.reload!` in an -initializer: +The code above will watch for changes based on `config/i18n.yml` and +`config/locales`. You can customize these options with +`I18nJS.listen(config_file: "config/i18n.yml", locales_dir: "config/locales")`. -```ruby -module I18n - def self.reload! - I18n::JS.backend.reload! - super - end -end -``` +### Integrating with your frontend -See issue [#428](https://github.com/fnando/i18n-js/issues/428) for more details -and discussion of this issue. +You're done exporting files, now what? Well, go to +[i18n](https://github.com/fnando/i18n) to discover how to use the NPM package +that loads all the exported translation. ## Maintainer -- Nando Vieira - <https://nandovieira.com> +- [Nando Vieira](https://github.com/fnando) -## Contributing +## Contributors -Once you've made your great commits: +- https://github.com/fnando/i18n-js/contributors -1. [Fork](https://help.github.com/forking/) I18n.js -2. Create a branch with a clear name -3. Make your changes (Please also add/change spec, README and CHANGELOG if - applicable) -4. Push changes to the created branch -5. [Create an Pull Request](https://github.com/fnando/i18n-js/pulls) -6. That's it! +## Contributing -Please respect the indentation rules and code style. And use 2 spaces, not tabs. -And don't touch the versioning thing. +For more details about how to contribute, please read +https://github.com/fnando/i18n-js/blob/main/CONTRIBUTING.md. -## Running tests - -You can run I18n tests using Node.js or your browser. - -To use Node.js, install the `jasmine-node` library: - - $ npm install jasmine-node - -Then execute the following command from the lib's root directory: - - $ npm test - -To run using your browser, just open the `spec/js/specs.html` file. - -You can run both Ruby and JavaScript specs with `rake spec`. - ## License -(The MIT License) +The gem is available as open source under the terms of the +[MIT License](https://opensource.org/licenses/MIT). A copy of the license can be +found at https://github.com/fnando/i18n-js/blob/main/LICENSE.md. -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the 'Software'), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +## Code of Conduct -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Everyone interacting in the i18n-js project's codebases, issue trackers, chat +rooms and mailing lists is expected to follow the +[code of conduct](https://github.com/fnando/i18n-js/blob/main/CODE_OF_CONDUCT.md).