i18n-js

repository·main·Indexed 42 days ago

https://github.com/fnando/i18n

A small JavaScript library providing internationalization (I18n) functionality similar to the Ruby i18n gem. Version 4.5.3 supports translation with interpolation, pluralization, and localized formatting for numbers, currency, and dates via the I18n class.

Tokens
2.8K
Snippets
13
Records
19
Agent score
52%

What's inside i18n-js

  1. Initialize the I18n instance

    main

    To use the library, instantiate the I18n class with a translations object. The object structure follows the Ruby on Rails i18n format, where each root key represents a locale (e.g., en, pt-BR, or zh-Hant-TW).

    You can provide a full translation object or a basic one for simple use cases.

    import { I18n } from "i18n-js";
    import translations from "./translations.json";
    
    const i18n = new I18n(translations);
  2. Install i18n-js via npm or yarn

    main

    You can install the i18n-js library using either npm or yarn. It is recommended to install the latest version.

    npm install i18n-js@latest
    # or
    yarn add i18n-js@latest
  3. Load base translations (rails-i18n)

    main

    The library supports loading base translations (like those from rails-i18n) to enable standard formatting for dates, numbers, and sentence connectors.

    import { I18n } from "i18n-js";
    import ptBR from "i18n-js/json/pt-BR.json";
    import en from "i18n-js/json/en.json";
    
    const i18n = new I18n({
      ...ptBR,
      ...en,
    });
  4. Convert flat JSON structures to nested objects for I18n.js

    main

    I18n.js requires a nested object structure to represent the translation tree. If your translation data uses a flat structure with dot-notation keys (e.g., "en.messages.hello": "hello"), you must transform it into a nested object before passing it to the library.

    You can achieve this using lodash.set or the flat package.

    const { set } = require("lodash");
    
    const from = {
      "en.messages.hello": "hello",
      "pt-BR.messages.hello": "olá",
    };
    
    function flatToNestedObject(target) {
      const nested = {};
    
      Object.keys(target).forEach((path) => set(nested, path, target[path]));
    
      return nested;
    }
    
    console.log(flatToNestedObject(from));
    // Output:
    // {
    //   en: { messages: { hello: 'hello' } },
    //   'pt-BR': { messages: { hello: 'olá' } }
    // }
  5. Fix 'Unable to resolve "make-plural"' error in React Native

    main

    If you encounter the error Unable to resolve "make-plural" from "node modules/i18n-js/dist/import/Pluralization.js", it is because the make-plural dependency uses .mjs files which your build pipeline might not be configured to recognize.

    In React Native environments, you must update your metro.config.js to include mjs in the sourceExts array.

    const { getDefaultConfig } = require("metro-config");
    
    module.exports = (async () => {
      const {
        resolver: { assetExts, sourceExts },
      } = await getDefaultConfig();
    
      return {
        resolver: {
          sourceExts: [...sourceExts, "mjs"],
        },
      };
    })();
  6. Resolve JSON SyntaxErrors in translation files

    main

    Errors like SyntaxError: Unexpected end of JSON input or Uncaught SyntaxError: Unexpected token ; are typically caused by invalid JSON files rather than i18n-js itself.

    • Empty Files: Ensure you are not attempting to import an empty .json file.
    • Invalid Syntax: Ensure your files contain valid JSON data. For example, writing {}; is invalid JavaScript/JSON and will trigger a syntax error. JSON must be a valid object or array without trailing semicolons.
  7. Update the translation store

    main

    You can dynamically update or lazy-load translations by calling I18n#store(translations). This merges the provided object into the existing store.

    import { I18n } from "i18n-js";
    import ptBR from "translations/pt-BR.json";
    import en from "translations/en.json";
    
    const i18n = new I18n();
    
    i18n.store(en);
    i18n.store(ptBR);
  8. Format numbers and currency

    main

    The library provides several helpers for localized number formatting:

    • I18n#numberToCurrency(number, options): Formats a number as currency. Options include precision, unit, separator, delimiter, and format (e.g., %u%n).
    • I18n#numberToPercentage(number, options): Formats a number as a percentage.
    • I18n#numberToDelimited(number, options): Formats a number with thousands delimiters. Supports delimiterPattern (a global regex) for custom grouping (e.g., Indian numbering system).
    • I18n#numberToRounded(number, options): Formats a number with specific precision and rounding modes (up, down, default).
    • I18n#numberToHumanSize(number, options): Converts bytes into human-readable units (e.g., KB, MB, GB).
    i18n.numberToCurrency(1234.56, { precision: 0 }); // "$1,235"
    
    i18n.numberToHumanSize(1234567); // "1.18 MB"
    
    i18n.numberToDelimited(12345678, { delimiter: "." }); // "12.345.678"
  9. Format dates and times

    main

    Use I18n#localize (alias I18n#l) to format dates, strings, or epoch integers based on defined formats in your translation object.

    • Input types: String (ISO-8601 or custom), Epoch integer, or Date object.
    • Custom Placeholders: You can include placeholders like %{day} in your date format definitions and pass them in the options.
    • Direct Helpers: Use I18n#toTime for parsing and I18n#strftime for standard strftime-style formatting.
    // Using a format defined in translations
    i18n.l("date.formats.short", new Date());
    
    // Using strftime
    i18n.strftime(new Date(), "%d/%m/%Y");
    
    // Relative time
    i18n.timeAgoInWords(fromTimestamp, toTimestamp); // "about 1 hour"
  10. Subscribe to I18n change events

    main

    A change event is triggered whenever I18n#store, I18n#update, I18n#locale, or I18n#defaultLocale is called. Use I18n#onChange(callback) to subscribe. The method returns an unsubscribe function to remove the handler. Additionally, I18n#version increments on every change, making it useful as a cache key.

    const i18n = new I18n();
    const unsubscribe = i18n.onChange(() => {
      console.log("I18n has changed!");
    });
    
    // To remove the listener:
    unsubscribe();
  11. Translate messages with I18n#t

    main

    Use I18n#t (or its alias I18n#translate) to fetch translations.

    • Scopes: You can pass a string ("greetings.hello") or an array (["greetings", "hello"]).
    • Interpolation: Use %{key} in your translation strings and pass an object containing the values to t().
    • Array Interpolation: If a translation entry is an array of strings, values are interpolated into each string in the array.
    • Custom Interpolation: You can override I18n#interpolate to support custom logic, such as returning React elements.
    // Basic translation
    i18n.t("hello");
    
    // Interpolation
    const i18n = new I18n({
      en: { greetings: "Hi, %{name}!" },
    });
    i18n.t("greetings", { name: "John" });
    
    // Array interpolation
    const i18n = new I18n({
      en: { messages: ["Hello!", "Welcome, %{name}!" ] },
    });
    i18n.t("messages", { name: "John" });
  12. Use pluralization with I18n#t

    main

    Pluralization uses special keywords in your translation files: zero, one, and other. To trigger pluralization, pass a count option to I18n#t.

    To support languages with different pluralization rules (like Russian), register a custom handler using I18n#pluralization.register(locale, handler).

    const i18n = new I18n({
      en: {
        inbox: {
          zero: "You have no messages",
          one: "You have one message",
          other: "You have %{count} messages",
        },
      },
    });
    
    i18n.t("inbox", { count: 0 }); // "You have no messages"