Coder Social home page Coder Social logo

tap-i18n's Introduction

tap-i18n

A comprehensive internationalization solution for Meteor

Internationalization for Meteor

tap-i18n is a Meteor package that provides a comprehensive i18n solution for Meteor apps and packages, with the goal of standardizing the way package developers internationalize their packages.

Watch a talk about tap:i18n & tap:i18n-db

Developed by: MeteorSpark Professional Meteor Services for TAPevents.

Maintained by: JustDo.com Project Management Tool.

Related Packages:

  • Check tap:i18n-db for Meteor collections internationalization.
  • Check tap:i18n-ui for bootstrap based UI components for tap:i18n.
  • Check tap:i18n-bundler for Cordova & static file deployments.

Contents

Key Features

All Encompassing

tap-i18n is designed in a way that distinguishes the role of the package developer, that is, making the package available in multiple languages, from the role of the app developer which is to translate the app, but more importantly, to manage the app's internationalization aspects, such as: setting the supported languages for the project, setting the client language, configuring CDNs for language files, and so on.

Readable Syntax

<div class="btn">{{_ "sign_up"}}</div>

Advanced i18n

tap-i18n uses i18next v1.11 as its internationalization engine and exposes all its capabilities to the Meteor's templates - variables, dialects, count/context aware keys, and more.

client/messages.html

<template name="messages_today">
  <p>{{_ "inbox_status" "Daniel" count=18}}</p>
</template>

i18n/en.i18n.json

{
  "inbox_status": "Hey, %s! You have received one new message today.",
  "inbox_status_plural": "Hey, %s! You have received %s new messages today."
}

See more examples below.

Transparent Namespacing

You don't need to worry about domain prefixing or package conflicts when you translate your project or package. Behind the scenes we automatically generate scoped namespaces for you.

Ready to Scale

  • Translations are unified into a single JSON file per language that includes both package and project-level translations
  • On-demand: translations are loaded only when they are needed
  • 3rd Party CDN Support

Quickstart

Step 1: Install tap-i18n using meteor:

$ meteor add tap:i18n

Step 2: Add translation helpers to your markup:

*.html

<div>{{_ "hello"}}</div>

Step 3: Define translations in JSON or YAML format:

i18n/en.i18n.json

{ "hello": "Hey there" }

i18n/fr.i18n.json

{ "hello": "Bonjour" }

i18n/es.i18n.yml

hello: Hola

Translations files should end with lang_tag.i18n.json/yml.

You can split translations of a certain language to multiple files, we ignore the prefixed text, e.g., we add the translations of menu.en.i18n.json in the same way we add those of en.i18n.json .

You can put languages files anywhere in your project tree, as long as they are common to both your server and client - do not put languages files under /client, /server or /public.

Note: Languages files have to be saved in utf-8 encoding.

Step 4: Initiate the client language on startup (optional)

If you want the client to be served by a specific language on startup

Assuming that you have a function getUserLanguage() that returns the language for tag for the current user.

getUserLanguage = function () {
  // Put here the logic for determining the user language

  return "fr";
};

if (Meteor.isClient) {
  Meteor.startup(function () {
    Session.set("showLoadingIndicator", true);

    TAPi18n.setLanguage(getUserLanguage())
      .done(function () {
        Session.set("showLoadingIndicator", false);
      })
      .fail(function (error_message) {
        // Handle the situation
        console.log(error_message);
      });
  });
}
  • If you won't set a language on startup your project will be served in the fallback language: English
  • You probably want to show a loading indicator until the language is ready (as shown in the example), otherwise the templates in your projects will be in English until the language will be ready

Documentation & Examples

TAPi18n API

TAPi18n.setLanguage(language_tag) (Client)

Sets the client's translation language.

Returns a jQuery deferred object that resolves if the language load succeed and fails otherwise.

Notes:

  • language_tag has to be a supported Language.
  • jQuery deferred docs: jQuery Deferred

TAPi18n.getLanguage() (Client)

Returns the tag of the client's current language or null if tap-i18n is not installed.

If inside a reactive computation, invalidate the computation the next time the client language get changed (by TAPi18n.setLanguage)

TAPi18n.getLanguages() (Anywhere)

Returns an object with all the supported languages and their names.

A language is considred supported if it is in the supported_languages array of the project-tap.i18n json. If supported_languages is null or not defined in project-tap.i18n we consider all the languages we find *.i18n.json/yml files to as supported.

The returned object is in the following format:

{
  'en': {
    'name':'English', // Local name
    'en':'English'    // English name
  },
  'zh': {
    'name':'中文'     // Local name
    'en':'Chinese'    // English name
  }
  .
  .
  .
}

TAPi18n.__(key, options, lang_tag=null) (Anywhere)

If lang_tag is null:

Translates key to the current client's language. If inside a reactive computation, invalidate the computation the next time the client language get changed (by TAPi18n.setLanguage).

Otherwise:

Translates key to lang_tag. if you use lang_tag you should use __ in a reactive computation since the string will be translated to the current client language if a translator to lang_tag is not ready in the client (if called for the first time with that lang_tag, or until language data load from the server finishes) and will get invalidated (trigger reactivity) when the translator to that lang_tag is ready to be used to translate the key.

Using i18next.t lng option or lang, which we made as alias to lang in tap:i18n, is equivalent to setting the lang_tag attribute.

The function is a proxy to the i18next.t() method. Refer to the documentation of i18next.t() to learn about its possible options. (Make sure you refer to i18next v1.11 documentation and not v2)

On the server, TAPi18n.__ is not a reactive resource. You have to specify the language tag you want to translate the key to.

TAPi18n.loadTranslations(translations, namespace="project") (Anywhere)

Use translations in addition or instead of the translations defined in the i18n.json files. Translations defined by loadTranslations will have priority over those defined in language files (i18n.json) of namespace (the project, or package name).

To enjoy the benefits of tap:i18n, you should use language files to internationalize your project whenever you can.

Legitimate cases for loadTranslations are:

  • Allowing users to change the project translations
  • Changing translations of 3rd party packages that you don't want to fork (see the Note below).

Example:

TAPi18n.loadTranslations(
    {
        es: {
            meteor_status_waiting: "Desconectado"
        },
        fr: {
            meteor_status_failed: "La connexion au serveur a échoué"
        }
    },
    "francocatena:status"
);

Arguments:

  • translations: An object of the following format:
{
    'lang-tag': {
        'translation-key1': 'translation',
        'translation-key2': 'translation',
        ...
    },
    ...
}
  • namespace="project": The namespace you want to add the translations to. by default translations are added to the project namespace, if you want to change a package translation use the package name as the namespace like the above example.

Notes:

  • Adding support to a new language in your app: You can't use addTranslations in order to add support to a new language, that is, to allow users to change the interface language of the app to that language. In order to start support a new language in your app, you'll have to either add a language file to that language (*.i18n.json file) or add that languages to your project-tap.i18n file.

  • Translating a package that uses tap:i18n to another language: If you want to add a new language to a 3rd party package (and you can't get it's owner to merge your pull request) consider introducing a "translation" package in which package-tap.i18n has the "namespace" options set to the package you are translating. That way you can translate with languages files instead of addTranslations and share your translation package with others.

The tap-i18n Helpers

The _ Helper

To use tap-i18n to internationalize your templates you can use the _ helper that we set on the project's templates and on packages' templates for packages that uses tap-i18n:

{{_ "key" "sprintf_arg1" "sprintf_arg2" ... op1="option-value" op2="option-value" ... }}

You can customize the helper name, see "Configuring tap-i18n" section.

The translation files that will be used to translate key depends on the template from which it is being used:

  • If the helper is being used in a template that belongs to a package that uses tap-i18n we'll always look for the translation in that package's translation files.
  • If the helper is being used in one of the project's templates we'll look for the translation in the project's translation files (tap-i18n has to be installed of course).

Usage Examples:

Assuming the client language is en.

Example 1: Simple key:

en.i18n.json:
-------------
{
    "click": "Click Here",
    "html_key": "<b>BOLD</b>"
}

page.html:
----------
<template name="x">
    {{_ "click"}}
    {{{_ "html_key"}}} <!-- Note that we use triple {{{ for html rendering -->
</template>

output:
-------
Click Here
<b>BOLD</b>

Example 2: Simple key specific language:

en.i18n.json:
-------------
{
    "click": "Click Here"
}

fr.i18n.json:
-------------
{
    "click": "Cliquez Ici"
}

page.html (lng and lang options are the same in tap:i18n you can use both):
----------
<template name="x">
    {{_ "click" lang="fr"}} 
</template>

<template name="x">
    {{_ "click" lng="fr"}} 
</template>

output:
-------
Cliquez Ici

Example 3: Sprintf:

en.i18n.json:
-------------
{
    "hello": "Hello %s, your last visit was on: %s"
}

page.html:
----------
<template name="x">
    {{_ "hello" "Daniel" "2014-05-22"}}
</template>

output:
-------
Hello Daniel, your last visit was on: 2014-05-22

Example 4: Named variables and sprintf:

en.i18n.json:
-------------
{
    "hello": "Hello __user_name__, your last visit was on: %s"
}

page.html:
----------
<template name="x">
    {{_ "hello" "2014-05-22" user_name="Daniel"}}
</template>

output:
-------
Hello Daniel, your last visit was on: 2014-05-22

Note: Named variables have to be after all the sprintf parameters.

Example 5: Named variables, sprintf, singular/plural:

en.i18n.json:
-------------
{
    "inbox_status": "__username__, You have a new message (inbox last checked %s)",
    "inbox_status_plural": "__username__, You have __count__ new messages (last checked %s)"
}

page.html:
----------
<template name="x">
    {{_ "inbox_status" "2014-05-22" username="Daniel" count=1}}
    {{_ "inbox_status" "2014-05-22" username="Chris" count=4}}
</template>

output:
-------
Daniel, You have a new message (inbox last checked 2014-05-22)
Chris, You have 4 new messages (last checked 2014-05-22)

Example 6: Singular/plural, context:

en.i18n.json:
-------------
{
    "actors_count": "There is one actor in the movie",
    "actors_count_male": "There is one actor in the movie",
    "actors_count_female": "There is one actress in the movie",
    "actors_count_plural": "There are __count__ actors in the movie",
    "actors_count_male_plural": "There are __count__ actors in the movie",
    "actors_count_female_plural": "There are __count__ actresses in the movie",
}

page.html:
----------
<template name="x">
    {{_ "actors_count" count=1 }}
    {{_ "actors_count" count=1 context="male" }}
    {{_ "actors_count" count=1 context="female" }}
    {{_ "actors_count" count=2 }}
    {{_ "actors_count" count=2 context="male" }}
    {{_ "actors_count" count=2 context="female" }}
</template>

output:
-------
There is one actor in the movie
There is one actor in the movie
There is one actress in the movie
There are 2 actors in the movie
There are 2 actors in the movie
There are 2 actresses in the movie
  • Refer to the documentation of i18next.t() v1.11 to learn more about its possible options. (Make sure you refer to i18next v1.11 documentation and not v2)
  • The translation will get updated automatically after calls to TAPi18n.setLanguage().

More helpers

{{languageTag}}:

The {{languageTag}} helper calls TAPi18n.getLanguage().

It's useful when you need to load assets depending on the current language, for example:

<template name="example">
  <img src="welcome_{{languageTag}}.png">
</template>

Languages Tags and Translations Prioritization

We use the IETF language tag system for languages tagging. With it developers can refer to a certain language or pick one of its dialects.

Example: A developer can either refer to English in general using: "en" or to use the Great Britain dialect with "en-GB".

If tap-i18n is install we'll attempt to look for a translation of a certain string in the following order:

  • Language dialect, if specified ("pt-BR")
  • Base language ("pt")
  • Base English ("en")

Notes:

  • We currently support only one dialect level. e.g. nan-Hant-TW is not supported.
  • "en-US" is the dialect we use for the base English translations "en".
  • If tap-i18n is not installed, packages will be served in English, the fallback language.

Structure of Languages Files

Languages files should be named: arbitrary.text.lang_tag.i18n.json . e.g., en.i18n.json, menu.pt-BR.i18n.json.

You can have more than one file for the same language.

You can put languages files anywhere in your project tree, as long as they are common to both your server and client - do not put languages files under /client, /server or /public.

Example for languages files:

en.i18n.json
{
    "sky": "Sky",
    "color": "Color"
}

pt.i18n.json
{
    "sky": "Céu",
    "color": "Cor"
}

fr.i18n.json
{
    "sky": "Ciel"
}

en-GB.i18n.json
{
    "color": "Colour"
}
  • Do not use colons and periods (see note below) in translation keys.
  • To avoid translation bugs all the keys in your package must be translated to English ("en") which is the fallback language we use if tap-i18n is not installed, or when we can't find a translation for a certain key.
  • In the above example there is no need to translate "sky" in en-GB which is the same in en. Remember that thanks to the Languages Tags and Translations Prioritization (see above) if a translation for a certain key is the same for a language and one of its dialects you don't need to translate it again in the dialect file.
  • The French file above have no translation for the color key above, it will fallback to English.
  • Check i18next features documentation for more advanced translations structures you can use in your JSONs files (Such as variables, plural form, etc.). (Make sure you refer to i18next v1.11 documentation and not v2)

A note about dot notation

Note that {_ "foo.bar"} will be looked under {foo: {bar: "Hello World"}}, and not under "foo.bar".

Configuring tap-i18n

To configure tap-i18n add to it a file named project-tap.i18n.

This JSON can have the following properties. All of them are optional. The values bellow are the defaults.

project-root/project-tap.i18n
-----------------------------
{
    "helper_name": "_",
    "supported_languages": null,
    "i18n_files_route": "/tap-i18n",
    "preloaded_langs": []
}

Options:

helper_name: the name for the templates' translation helper.

supported_languages: A list of languages tags you want to make available on your project. If null, all the languages we'll find translation files for, in the project, will be available.

i18n_files_route: The route in which the tap-i18n resources will be available in the project.

preloaded_langs: An array of languages tags. If isn't empty, a single synchronous ajax requrest will load the translation strings for all the languages tags listed. If you want to load all the supported languages set preloaded_langs to ["*"] ("*" must be the first item of the array, the rest of the array will be ignored. ["zh-*"] won't work). An alernative way to dynamically set preloaded_langs on runtime is by defining the TAP_I18N_PRELOADED_LANGS global variable. This variable should be an array of language tags.

<head>
  <script>
    TAP_I18N_PRELOADED_LANGS = ["pt-BR", "zh-TW", "he"];
  </script>
  <!-- ... The JS Bundle of your Meteor APP ... -->
</head>

Notes:

  • English is loaded by default, so you don't need to include it in the preloaded language list.
  • We use AJAX to load the languages files so you'll have to set CORS on your CDN.
  • Support of TAP_I18N_PRELOADED_LANGS relies on the project-tap.i18n build plugin. In other words, if project-tap.i18n doesn't exist, TAP_I18N_PRELOADED_LANGS will have no effect.
  • Preloaded languages are the union of preloaded_langs (specified in project-tap.i18n) and TAP_I18N_PRELOADED_LANGS (specified in the <head>). Examples:
    1. preloaded_langs = ["fr"] TAP_I18N_PRELOADED_LANGS = ["he"] Result: "fr" and "he" will be preloaded
    2. preloaded_langs = ["*"] All languages will be preloaded, regardless of whether TAP_I18N_PRELOADED_LANGS is defined.
    3. preloaded_langs = ["ar"] TAP_I18N_PRELOADED_LANGS = ["ar", "vi"] Result: "ar" and "vi" will be preloaded.

Configuring CDN in tap-i18n

To utilize a Content Delivery Network (CDN) with tap:i18n, invoke TAPi18n.setCdnCb(cb). This function expects cb, a callback function, which receives a URL path as its parameter and should return the CDN-modified URL.

Important: If your project incorporates project-tap.i18n, it is crucial to execute TAPi18n.setCdnCb(cb) before project-tap.i18n loads. Arrange your configuration files such that the one for CDN setup is named in a way that it loads lexicographically prior to project-tap.i18n.

For instance, in JustDo.com, the CDN setup is managed as follows:

# /lib/030-i18n/000-tap-i18n-cdn-loader.coffee
TAPi18n.setCdnCb JustdoCoreHelpers.getCDNUrl

This configuration file is placed lexicographically before /lib/030-i18n/project-tap.i18n in the directory structure, ensuring the correct load order.

Disabling tap-i18n

Step 1: Remove tap-i18n method calls from your project.

Step 2: Remove tap-i18n package

$ meteor remove tap:i18n

Using tap-i18n in Cordova apps

In order to use tap-i18n in a Cordova app you must set the --server flag to your server's root url when building your project.

$ meteor build --server="http://www.your-site-domain.com"

If your app should work when the user is offline, install the tap:i18n-bundler package and follow its instructions.

Developing Packages

Though the decision to translate a package and to internationalize it is a decision made by the package developer, the control over the internationalization configurations are done by the project developer and are global to all the packages within the project.

Therefore if you wish to use tap-i18n to internationalize your Meteor package your docs will have to refer projects developers that will use it to the "Usage - Project Developers" section above to enable internationalization. If the project developer won't enable tap-i18n your package will be served in the fallback language English.

tap-i18n Two Work Modes

tap-i18n can be used to internationalize projects and packages, but its behavior is determined by whether or not it's installed on the project level. We call these two work modes: enabled and disabled.

When tap-i18n is disabled we don't unify the languages files that the packages being used by the project uses, and serve all the packages in the fallback language (English)

Setup tap-i18n

In order to use tap-i18n to internationalize your package:

Step 1: Add the package-tap.i18n configuration file:

You can use empty file or an empty JSON object if you don't need to change them.

The values below are the defaults.

package_dir/package-tap.i18n
----------------------------
{
    // The name for the translation function that
    // will be available in package's namespace.
    "translation_function_name": "__", 

    // the name for the package templates' translation helper
    "helper_name": "_", 
    
    // directory for the translation files (without leading slash)
    "languages_files_dir": "i18n",

    // tap:i18n automatically separates the translation strings of each package to a
    // namespace dedicated to that package, which is used by the package's translation
    // function and helper. Use the namespace option to set a custom namespace for
    // the package. By using the name of another package you can use your package to
    // add to that package or modify its translations. You can also set the namespace to
    // "project" to add translations that will be available in the project level.
    "namespace": null 
}

Step 2: Create your languages_files_dir:

Example for the default languages_files_dir path and its structure:

.
|--package_name
|----package.js
|----package-tap.i18n
|----i18n # Should be the same path as languages_files_dir option above
|------en.i18n.json
|------fr.i18n.json
|------pt.i18n.json
|------pt-BR.i18n.json
.
.
.

NOTE: the file for the fallback language (en.i18n.json) must exist (it may be empty though).

The leanest set up (for instance in a private package, where you keep the translations at the project level) is two empty files: package-tap.i18n and i18n/en.i18n.json.

Step 3: Setup your package.js:

Your package's package.js should be structured as follow:

Package.onUse(function (api) {
  api.use(["tap:i18n"], ["client", "server"]);

  .
  .
  .

  // You must load your package's package-tap.i18n before you load any
  // template
  api.addFiles("package-tap.i18n", ["client", "server"]);

  // Templates loads (if any)

  // List your languages files so Meteor will watch them and rebuild your
  // package as they change.
  // You must load the languages files after you load your templates -
  // otherwise the templates won't have the i18n capabilities (unless
  // you'll register them with tap-i18n yourself, see below).
  api.addFiles([
    "i18n/en.i18n.json",
    "i18n/fr.i18n.json",
    "i18n/pt.i18n.json",
    "i18n/pt-br.i18n.json"
  ], ["client", "server"]);
});

Note: en, which is the fallback language, is the only language we integrate into the clients bundle. All the other languages files will be loaded only to the server bundle and will be served as part of the unified languages files, that contain all the project's translations.

Package Level tap-i18n Functions

The following functions are added to your package namespace by tap-i18n:

__("key", options, lang_tag) (Anywhere)

Read documenation for TAPi18n.__ above.

On the server, TAPi18n.__ is not a reactive resource. You have to specify the language tag you want to translate the key to.

You can use package-tap.i18n to change the name of this function.

registerI18nHelper(template_name) (Client)

registerTemplate(template_name) (Client) [obsolete alias, will be removed in future versions]

Register the _ helper that maps to the __ function for the template with the given name.

Important: As long as you load the package templates after you add package-tap.i18n and before you start adding the languages files you won't need to register templates yourself.

Using tap-i18n in Your Package Templates

See "The tap-i18n helper" section above.

Unit Testing

See /unittest/test-packages/README.md .

License

MIT

Author

Daniel Chcouri

Contributors

Credits

tap-i18n's People

Contributors

danieljonce avatar davidjytang avatar francocatena avatar hamoid avatar iovecoldpizza avatar istoramandiri avatar krishaamer avatar laosb avatar marcelpanse avatar mpowaga avatar pistou avatar sachag avatar shkomg avatar theosp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tap-i18n's Issues

supporting multiple nested translation files

It would be neat if you could support multiple files with same "namespaces" like this:

{
  "module": {
      "submodule1": {
           "translation": "lalala"
      }
}

{
  "module": {
      "submodule2": {
           "translation": "lalala"
      }
}

Right now these should be in same file like so:

{
  "module": {
      "submodule1": {
           "translation": "lalala"
      },
      "submodule2": {
           "translation": "lalala"
      }
}

But being able to split them into several files would be nice =)

Language file sets underscore to global scope

I've noticed that language files are setting _ to the global scope. I wouldn't say it is a real problem in a default Meteor setup, as underscore is already embedded in the project. But I've overridden underscore with lodash early in the execution pipeline. So reassigning _ to underscore instead of lodash creates some conflicts in my code like 'undefined' method exceptions.

It would be great if this could be solved.

// http://mydomain.com/i18n/en.i18n.js
(function(){_ = Package.underscore._; // « the problem line
TAPi18n._enable({"helper_name":"_","supported_languages":null,"i18n_files_route":"/tap-i18n","cdn_path":null});

Couldn't load language 'es' JSON: parsererror

I'm getting the following error "Couldn't load language 'es' JSON: parsererror"

first everything works fine both no local host and production however if I'm offline I'm getting the error and the app is not translating.

I'm using app cache and I believe it has something to do with it. Current version tap:i18n 1.4.0

This also might be related #6

getting rid of Ajax calls?

what would you think about using a dedicated i18n collection to store all the json docs for available languages and than play with publish/subscribe to actually get the data for the selected language on the client?

I feel this would be a better meteoric way to load i18n data on the client...
But this is almost random thoughts, I haven't had a chance do dig your code ;-)

How to call translation helpers on js?

I use simple-schema with auto-form to generate form like this:

Branch = new SimpleSchema({
    khName: {
        type: String,
        label: "Kh Name", // {{_ "khName"}
        //unique: true,
        max: 200
    },
    khShortName: {
        type: String,
        label: "Kh Short Name",
        //unique: true,
        max: 200
    },
........................

So i want to call translation on js of simple-schema.

has no method 'en' Error when trying to translate

Hello,

After hours of trying and having like 3 hours sleep I decided to hand my problem over to you guys:

Im getting this error:

W20150330-04:32:32.173(2)? (STDERR) TypeError: Object #<Object> has no method 'en'

When I want to invoke the TAPi18n.__ method from an smart package

This is how my package.js looks like:

Package.onUse(function(api) {
    api.use(['tap:i18n', 'vazco:universe-html-purifier', 'wtfzn:dompurify']);
    api.use(["session"], "client");

    api.addFiles(['package-tap.i18n']);

    api.addFiles([
        'i18n/nl.i18n.json',
        'i18n/en.i18n.json',
        'i18n.js',
        'myapp-common.js'
    ]);

    api.export([
        'purify',
        'trans',
        'validEmail'
    ]);

});

And this is the trans method which uses the __ method

trans = function (str, options) {
    if (Meteor.isServer) {
        return TAPi18n.__(str, options, 'nl');
    } else {
        return TAPi18n.__(str, options);
    }
};

Hope you guys can help me. I really need my sleep right now ;p


It looks like the problems is at the server side, client side doesnt give any errors

Deploy on modulus or meteor up

Hi,

I need to deploy my app to modulus or meteor up but I get this error:

Couldn't load language 'es' JSON: parsererror

"es.i18n.json" not found when deploying on meteor.com

Hi @theosp, I've created an Spanish translation file called "es.i18n.json". The project works well when executed locally, but when deploying on meteor.com Google Chrome console says:

GET http://<project_name>.meteor.com/tap-i18n/es.json 404 (Not Found)

It seems that the original file "es.i18n,json" has been truncated to just "es.json".
Any idea?
Thanks!

Add events support

Next version of tap:i18n is going to support events using Node's EventEmitter.

You are welcome to suggest events you think will be useful.

Thanks,
-Daniel

Parameter zero produces bad value

TAPi18n.__('You have earned %s points', 0)

produces:

"You have earned %s points"

I have tried %d but the result is the same.

Expected behavior:

"You have earned 0 points"

__ function

I don't have much experience with i18n but is there a reason to a function be named __?
Why not t and/or translate?

It feels like obfuscation to me.

upgrade package to 0.9.0

I would be happy to see this package upgraded to meteor 0.9.0.

ps. is it possible to use this package, when deploying the app on modulus.io, any experience here?

Translations load as one file

In the readme it says "On-demand: translations are loaded only when they are needed". I took that to understand that strings would be loaded in small batches according to the needs of a template. However, it seems that it just loads the entire language file in one go, which is a problem as I have a rather large application with over a thousand strings.

Am I missing something or is that just how it works?

Language fallback doesn't seem to work

I have a file zh.i18n.json in my languages folder, but when i do TAPi18n.setLanguage('zh-HK') it falls back to english instead of the available zh file.

If i set TAPi18n.setLanguage('zh') it works though.

A note beside: my zh file has not all properties like the english one set, but this should make any difference here right?

Package Translation

Hi,
I have multiple packages with translations. Only the last package I added to the meteor project will be translated. Has anyone the same problem? I use meteor 0.9.4.

Allow users to define preloaded languages

At the moment, only the default language (en) is preloaded. So if my site's default language isn't en, then the JSON file doesn't get pre-loaded and I may get a momentary flash where the placeholder text can be seen.

Thus, I propose an enhancement where we allow the user to define which languages should be preloaded.

upgraded to 1.0.0 and get the following error

While building the application:
packages/tap-i18n-compiler/lib/plugin/helpers/load_json.coffee:19:18: Meteor is not defined (compiling project-tap.i18n)
  at Object._.extend.loadJSON (packages/tap-i18n-compiler/lib/plugin/helpers/load_json.coffee:19:18)
  at __coffeescriptShare (packages/tap-i18n-compiler/lib/plugin/compilers/project-tap.i18n.coffee:49:29)

Multiple Key Support or Chaining

It would be great to be able to have more then one key or chaining I'm doing a lot of this

TAPi18n.('nameNotAString', {}, currentLang) + ': ' + TAPi18n.('row', {}, currentLang) + ' ' + (parseInt(key)+2) +', '+ dataset.data[key].name)

if I could just do this TAPi18n.__('nameNotAString', 'row' , {}, currentLang)

Is it working??

Hi, I've just tried the package with the "Example 1-Simple key" and the output is "click" instead of "Click Here". Thanks!

Zero plural form isn't defined for all languages

I'm trying to use keys like "key", "key_plural_0", "key_plural_2" to show "none", "singular", "plural" but it seems like I can only get singular and plural, nothing else.

Are the "multiple plural forms" implemented as explained here?
http://i18next.com/pages/doc_features.html

My code looks like this:

{{_ "revision" count=getDiscussionCount}}

...
"revision": "__count__ Beitrag",
"revision_plural_0": "Keine Beiträge",
"revision_plural_2": "__count__ Beiträge",
...

when used inside select, _ helper is not reactive

When I do this:

<p>{{_ "Good type"}}</p>
<div class="form-inline">
        <span class="small">
            <span class="selectwrap">
                <select class="search-select" id="type">
                    <option value="">{{_ "Good type"}}</option>
                </select>

            </span>
        </span>
</div>

When I change language, the value inside the

tag is automatically update, but the value in the select statement is not updated.

How can I fix it?

This is my event handler:

Template.header_top.events ({
  "click .select_language": function(e, instance) {
    setLanguage($(e.target).data("language"));
  }
})

Thanks.

Assigning helper with Template.xyz._ is deprecated

I'm getting this warning in the console when using tap:i18n in a package:

 W20141124-11:04:41.452(9) (blaze.js:67) Warning: Assigning helper with `Template.notificationsMenu._ = ...` is deprecated.  Use `Template.notificationsMenu.helpers(...)` instead.
moment.js:316 Deprecation warning: moment.lang is deprecated. Use moment.locale instead.

Translate metadata

I don't know if this should be considered as an issue, but I am unable to use tap-i18n for translating metadata in <head /> (title, description, etc.).

So how to deal with metadata translation with tap-i18n, and more generally how to translate a meteor app in a 'seo-friendly' way with tap-i18n?

Using translation as argument

There is any way to use another translation as an argument?

Example:

en.i18n.json:
"header": {
"newButton": "New %s"
},
"label": {
"project": "Project",
"stream": "Stream",
"job": "Job"
}

I want to generate:
New Project, New Stream, New Job. Spacebars doesn't allow me to do something like this:
_ "header.newButton" _ "header.project". Any idea?

HTML tags in translations

HTML tags (, , ...) are not rendered. If my translation file is like:

{ "test": "<h1 class=\"special\">This is a <strong>test</strong></h1>"; }

Then the output for {{_ "test"}} will be:

<h1 class=\"special\">This is a <strong>test</strong></h1>

How can I ask tap-i18n to render HTML tags?

`getLanguages()` returning disabled languages

I have a project with two sets of language files in /i18n

  • en.i18n.json
  • zh.i18n.json

I have my /project-tap.i18n as such (only enabling en):

{
  "supported_languages" : ["en"]
}

TAPi18n.getLanguages() is expected return only {en: Object}, but returns {en: Object, zh: Object}

zh is definitely disabled:

> TAPi18n.setLanguage('zh').fail(function(err){console.log(err);})
> ["Language zh is not supported"]

aldeed:simple-schema - Constraints

Hi theosp

I like to update from tap:i18n 1.2.1 to tap:i18n 1.3.1
tap-i18n 1.3.1 is bound to aldeed:simple-schema --> 0.7.0.
My project is currently on the latest version aldeed:simple-schema --> 1.3.0.

This leads to an update error:

=> Errors while adding packages:

While selecting package versions:
error: conflict: constraints on aldeed:simple-schema cannot all be satisfied.
Constraints come from:
aldeed:[email protected]
aldeed:[email protected]
tap:[email protected]

Regards roman

keeping two languages around

following on from meteor-talk

Regarding your first questions, true, in Meteor tap:i18n language can only be A or B, but not a mixture, switched automatically based on a session. Right now there are no plans to implement this future in the near future. Again, if someone would want to implement this I can help with guidance.

If i want to have a UI that has two languages, how could this be done?

ie I would like to have the main app UI in a users native language but have some elements in a second (target) language.

TAPi18n.__(key, options, lang_tag) (Server) 
TAPi18n.__(key, options) (Client)

i see the server translate commands will take a langTag param, but not the client.
are the languages both in memory? would it be possible to add this to the client

I don't see anything in i18next
http://i18next.com/pages/doc_features.html
that would enable this either.

Feature Request: 'Scattered' i18n.json files

From a project development perspective, it would be excellent to be able to include the xx.i18n.json files anywhere in the folder structure of my Meteor project, so that translated UI and it's corresponding templates can be kept in the same place.

It would be especially good for larger projects that haven't packaged all of their code, but want to keep a degree of modularity using folder structure. It would probably also make things easier if you decided to package part of your project.

For me it might look something like this:

/module-name
  /client
    module-name.html
    module-name.coffee
  /i18n
    module-name.i18n.json

(Or even including the i18n file directly in the client folder.)

Anyways, just a thought; for some of my projects it'd be a really nice feature, and it feels like it follows the Meteor way.

Cheers Daniel, tap-i18n is great!

More then one file for same language

Hi, thanks for creating this package!

I'm having trouble with newest release and feature with multiple lang files scattered around project. Lets say I'm using en and de languages. So first I'm adding project-tap.i18n file to project root folder like:

{
    "helper_name": "_",
    "supported_languages": ["en", "de"],
    "i18n_files_route": "/i18n",
    "cdn_path": null
}

So all my generic translations go to: i18n/en.i18n.json and i18n/de.i18n.json. Now I want develop a module like under: modules/accoungs-sign/. So I'm adding new translation under: modules/accounts-sign/i18n/sign.en.i18n.json but every time I start project/add file I'm getting:

/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/dev_bundle/lib/node_modules/fibers/future.js:173
                        throw(ex);
                              ^
Error: Path reservation conflict: app/en.js
  at [object Object]._.extend.reserve (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/builder.js:249:15)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:914:15
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:906:11
  at Array.forEach (native)
  at Function._.each._.forEach (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/dev_bundle/lib/node_modules/underscore/underscore.js:79:11)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:905:11
  at Array.forEach (native)
  at Function._.each._.forEach (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/dev_bundle/lib/node_modules/underscore/underscore.js:79:11)
  at eachResource (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:904:9)
  at [object Object]._.extend.write (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:913:5)
  at writeTargetToPath (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:1484:12)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:1588:26
  at Function._.each._.forEach (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/dev_bundle/lib/node_modules/underscore/underscore.js:87:22)
  at writeSiteArchive (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:1587:7)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:1969:20
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/buildmessage.js:186:11
  at [object Object]._.extend.withValue (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/fiber-helpers.js:112:14)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/buildmessage.js:183:27
  at [object Object]._.extend.withValue (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/fiber-helpers.js:112:14)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/buildmessage.js:181:16
  at [object Object]._.extend.withValue (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/fiber-helpers.js:112:14)
  at Object.capture (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/buildmessage.js:172:21)
  at Object.exports.bundle (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/bundler.js:1689:31)
  at bundleApp (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/run-app.js:469:28)
  at [object Object]._.extend._runOnce (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/run-app.js:487:24)
  at [object Object]._.extend._fiber (/Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/run-app.js:673:28)
  at /Volumes/sys_user/.meteor/packages/meteor-tool/.1.0.31.o46iyo++os.osx.x86_64+web.browser+web.cordova/meteor-tool-os.osx.x86_64/tools/run-app.js:360:12


/usr/local/lib/node_modules/meteorite/lib/command.js:41
            throw "Command exited with " + code + "/" + signal;
                                                      ^
Command exited with 8/null

This:

Error: Path reservation conflict: app/pl.js

Will be replaced with any kind of language I'll point in filename, so app/de.js etc...

I'm missing something here? Thanks

Getting "Uncaught Error: No such function: _"

Thanks for creating tap-i18n! New to GitHub so if I should have reopened issue #15 instead of creating a new issue, please accept my apologies!

Running Meteor 1.0.1 (the latest) and added the tap-i18n package today. I placed the translation files in /public/locales/LANG/LANG.i18n.json but it returned with the No such function: _ error. Only when I moved the file to /locales/LANG/LANG.i18n.json did it work. Maybe in addition to Issue #15, the files cannot be placed in the /public folder neither?

Default Language

I'm using yoolab/iron-router-i18n to set the language depending on the url. The package uses a hook to set the language, but not for the default language. The configuration looks like this:

Router.configure({
  i18n: {
    defaultLanguage: 'de',
    languages: ['de', 'en'],
    setLanguage: function (lang) {
      if (Meteor.isClient) {
        TAPi18n.setLanguage(lang);
      }
    }
  }
});

I tried to use a startup script to set a default language for tap-i18n:

// both/startup.js
if (Meteor.isClient) {
  Meteor.startup(function () {
    TAPi18n.setLanguage('de');
  });
}

The problem is, that this startup script seems to override the hook when the app runs on a production server. I think this might be because of some async behavior. Is there a better way to set the default language than just using the setLanguage function?

If I get this package right, I'm looking for a way to set a custom fallback_language, that is not async.

pass object to helper

It would be great, if you could pass an object to the helper instead of hardcoding key-value pairs or plain strings. Like {{_ error_message object}}.

var metadata = {
  filename: "testfile.mp4"
};

Movies.insert(movie, function (err) {
  // err.message = "file_upload_error";
  // err.metadata = metadata
  ShowMessage.error(err.message, err.metadata);
});

// i18n/en.i18n.json
{
  "file_upload_error": "There was an error uploading the file _filename__!"
}

ShowMessage would be doing stuff which would render message templates which contain the translation helper somewhere.

[EDIT]: I would make a pull request, but unfortunately this project is in coffeescript :-/

Error in production "No such function: _"

Thank you for your great work in this project!

I am trying to deploy an app with tap:i18n, tap:i18n-ui and tap:i18n-db.
In development mode everything works fine, however, when the app is deployed and in production mode I get an error that "tap-i18n is not enabled in the project level" and "No such function: _".

This seems similar to #15

I guess that the 0.9.4 change how to declare helpers might have to do something with it: Meteor-Community-Packages/meteor-autoform#386

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.