GithubHelp home page GithubHelp logo

glebm / i18n-tasks Goto Github PK

View Code? Open in Web Editor NEW
2.1K 33.0 259.0 2.8 MB

Manage translation and localization with static analysis, for Ruby i18n

Home Page: http://glebm.github.io/i18n-tasks

License: MIT License

Ruby 99.99% Batchfile 0.01%
static-analysis static-code-analysis ruby i18n translation-management

i18n-tasks's Introduction

i18n-tasks Build Status Coverage Status Gitter

Stand With Ukraine

i18n-tasks helps you find and manage missing and unused translations.

This gem analyses code statically for key usages, such as I18n.t('some.key'), in order to:

  • Report keys that are missing or unused.
  • Pre-fill missing keys, optionally from Google Translate or DeepL Pro.
  • Remove unused keys.

Thus addressing the two main problems of i18n gem design:

  • Missing keys only blow up at runtime.
  • Keys no longer in use may accumulate and introduce overhead, without you knowing it.

Installation

i18n-tasks can be used with any project using the ruby i18n gem (default in Rails).

Add i18n-tasks to the Gemfile:

gem 'i18n-tasks', '~> 1.0.14', group: :development

Copy the default configuration file:

$ cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/

Copy rspec test to test for missing and unused translations as part of the suite (optional):

$ cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/

Or for minitest:

$ cp $(i18n-tasks gem-path)/templates/minitest/i18n_test.rb test/

Usage

Run bundle exec i18n-tasks to get the list of all the tasks with short descriptions.

Check health

i18n-tasks health checks if any keys are missing or not used, that interpolations variables are consistent across locales, and that all the locale files are normalized (auto-formatted):

$ i18n-tasks health

Add missing keys

Add missing keys with placeholders (base value or humanized key):

$ i18n-tasks add-missing

This and other tasks accept arguments:

$ i18n-tasks add-missing -v 'TRME %{value}' fr

Pass --help for more information:

$ i18n-tasks add-missing --help
Usage: i18n-tasks add-missing [options] [locale ...]
    -l, --locales  Comma-separated list of locale(s) to process. Default: all. Special: base.
    -f, --format   Output format: terminal-table, yaml, json, keys, inspect. Default: terminal-table.
    -v, --value    Value. Interpolates: %{value}, %{human_key}, %{value_or_human_key}, %{key}. Default: %{value_or_human_key}.
    -h, --help     Display this help message.

Translate Missing Keys

Translate missing keys using a backend service of your choice.

$ i18n-tasks translate-missing

# accepts backend, from and locales options
$ i18n-tasks translate-missing --from=base es fr --backend=google

Available backends:

Find usages

See where the keys are used with i18n-tasks find:

$ i18n-tasks find common.help
$ i18n-tasks find 'auth.*'
$ i18n-tasks find '{number,currency}.format.*'

Remove unused keys

$ i18n-tasks unused
$ i18n-tasks remove-unused

These tasks can infer dynamic keys such as t("category.\#{category.name}") if you set search.strict to false, or pass --no-strict on the command line.

If you want to keep the ordering from the original language file when using remove-unused, pass -k or --keep-order.

Normalize data

Sort the keys:

$ i18n-tasks normalize

Sort the keys, and move them to the respective files as defined by config.write:

$ i18n-tasks normalize -p

Move / rename / merge keys

i18n-tasks mv <pattern> <target> is a versatile task to move or delete keys matching the given pattern.

All nodes (leafs or subtrees) matching <pattern> are merged together and moved to <target>.

Rename a node (leaf or subtree):

$ i18n-tasks mv user account

Move a node:

$ i18n-tasks mv user_alerts user.alerts

Move the children one level up:

$ i18n-tasks mv 'alerts.{:}' '\1'

Merge-move multiple nodes:

$ i18n-tasks mv '{user,profile}' account

Merge (non-leaf) nodes into parent:

$ i18n-tasks mv '{pages}.{a,b}' '\1'

Delete keys

Delete the keys by using the rm task:

$ i18n-tasks rm 'user.{old_profile,old_title}' another_key

Compose tasks

i18n-tasks also provides composable tasks for reading, writing and manipulating locale data. Examples below.

add-missing implemented with missing, tree-set-value and data-merge:

$ i18n-tasks missing -f yaml fr | i18n-tasks tree-set-value 'TRME %{value}' | i18n-tasks data-merge

remove-unused implemented with unused and data-remove (sans the confirmation):

$ i18n-tasks unused -f yaml | i18n-tasks data-remove

Remove all keys from fr that do not exist in en. Do not change en:

$ i18n-tasks missing -t diff -f yaml en | i18n-tasks tree-mv en fr | i18n-tasks data-remove

See the full list of tasks with i18n-tasks --help.

Features and limitations

i18n-tasks uses an AST scanner for .rb and .html.erb files, and a regexp-based scanner for other files, such as .haml.

Relative keys

i18n-tasks offers support for relative keys, such as t '.title'.

✔ Keys relative to the file path they are used in (see relative roots configuration) are supported.

✔ Keys relative to controller.action_name in Rails controllers are supported. The closest def name is used.

Plural keys

✔ Plural keys, such as key.{one,many,other,...} are fully supported.

Reference keys

✔ Reference keys (keys with :symbol values) are fully supported. These keys are copied as-is in add/translate-missing, and can be looked up by reference or value in find.

t() keyword arguments

scope keyword argument is fully supported by the AST scanner, and also by the Regexp scanner but only when it is the first argument.

default argument can be used to pre-fill locale files (AST scanner only).

Dynamic keys

By default, dynamic keys such as t "cats.#{cat}.name" are not recognized. I encourage you to mark these with i18n-tasks-use hints.

Alternatively, you can enable dynamic key inference by setting search.strict to false in the config. In this case, all the dynamic parts of the key will be considered used, e.g. cats.tenderlove.name would not be reported as unused. Note that only one section of the key is treated as a wildcard for each string interpolation; i.e. in this example, cats.tenderlove.special.name will be reported as unused.

I18n.localize

I18n.localize is not supported, use i18n-tasks-use hints. This is because the key generated by I18n.localize depends on the type of the object passed in and thus cannot be inferred statically.

Configuration

Configuration is read from config/i18n-tasks.yml or config/i18n-tasks.yml.erb. Inspect the configuration with i18n-tasks config.

Install the default config file with:

$ cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/

Settings are compatible with Rails by default.

Locales

By default, base_locale is set to en and locales are inferred from the paths to data files. You can override these in the config.

Storage

The default data adapter supports YAML and JSON files.

Multiple locale files

i18n-tasks can manage multiple translation files and read translations from other gems. To find out more see the data options in the config. NB: By default, only %{locale}.yml files are read, not namespace.%{locale}.yml. Make sure to check the config.

For writing to locale files i18n-tasks provides 2 options.

Pattern router

Pattern router organizes keys based on a list of key patterns, as in the example below:

data:
  router: pattern_router
  # a list of {key pattern => file} routes, matched top to bottom
  write:
    # write models.* and views.* keys to the respective files
    - ['{models,views}.*', 'config/locales/\1.%{locale}.yml']
    # or, write every top-level key namespace to its own file
    - ['{:}.*', 'config/locales/\1.%{locale}.yml']
    # default, sugar for ['*', path]
    - 'config/locales/%{locale}.yml'
Conservative router

Conservative router keeps the keys where they are found, or infers the path from base locale. If the key is completely new, conservative router will fall back to pattern router behaviour. Conservative router is the default router.

data:
  router: conservative_router
  write:
    - ['devise.*', 'config/locales/devise.%{locale}.yml']
    - 'config/locales/%{locale}.yml'

If you want to have i18n-tasks reorganize your existing keys using data.write, either set the router to pattern_router as above, or run i18n-tasks normalize -p (forcing the use of the pattern router for that run).

Isolating router

Isolating router assumes each YAML file is independent and can contain similar keys.

As a result, the translations are written to an alternate target file for each source file (only the %{locale} part is changed to match target locale). Thus, it is not necessary to specify any write configuration (in fact, it would be completely ignored).

This can be useful for example when using ViewComponent sidecars (ViewComponent assigns an implicit scope to each sidecar YAML file but i18n-tasks is not aware of that logic, resulting in collisions):

  • app/components/movies_component.en.yml:

    en:
      title: Movies
  • app/components/games_component.en.yml

    en:
      title: Games

This router has a limitation, though: it does not support detecting missing keys from code usage (since it is not aware of the implicit scope logic).

Key pattern syntax

A special syntax similar to file glob patterns is used throughout i18n-tasks to match translation keys:

syntax description
* matches everything
: matches a single key
*: matches part of a single key
{a, b.c} match any in set, can use : and *, match is captured

Example of usage:

$ bundle exec i18n-tasks mv "{:}.contents.{*}_body" "\1.attributes.\2.body"

car.contents.name_body ⮕ car.attributes.name.body
car.contents.description_body ⮕ car.attributes.description.body
truck.contents.name_body ⮕ truck.attributes.name.body
truck.contents.description_body ⮕ truck.attributes.description.body

Custom adapters

If you store data somewhere but in the filesystem, e.g. in the database or mongodb, you can implement a custom adapter. If you have implemented a custom adapter please share it on the wiki.

Usage search

i18n-tasks uses an AST scanner for .rb and .html.erb files, and a regexp scanner for all other files. New scanners can be added easily: please refer to this example.

See the search section in the config file for all available configuration options. NB: By default, only the app/ directory is searched.

Fine-tuning

Add hints to static analysis with magic comment hints (lines starting with (#|/) i18n-tasks-use by default):

# i18n-tasks-use t('activerecord.models.user') # let i18n-tasks know the key is used
User.model_name.human

You can also explicitly ignore keys appearing in locale files via ignore* settings.

If you have helper methods that generate translation keys, such as a page_title method that returns t '.page_title', or a Spree.t(key) method that returns t "spree.#{key}", use the built-in PatternMapper to map these.

For more complex cases, you can implement a custom scanner.

See the config file to find out more.

Google Translate

i18n-tasks translate-missing requires a Google Translate API key, get it at Google API Console.

Where this key is depends on your Google API console:

  • Old console: API Access -> Simple API Access -> Key for server apps.
  • New console: Nav Menu -> APIs & Services -> Credentials -> Create Credentials -> API Keys -> Restrict Key -> Cloud Translation API

In both cases, you may need to create the key if it doesn't exist.

Put the key in GOOGLE_TRANSLATE_API_KEY environment variable or in the config file.

# config/i18n-tasks.yml
translation:
  backend: google
  google_translate_api_key: <Google Translate API key>

or via environment variable:

GOOGLE_TRANSLATE_API_KEY=<Google Translate API key>

DeepL Pro Translate

i18n-tasks translate-missing requires a DeepL Pro API key, get it at DeepL.

# config/i18n-tasks.yml
translation:
  backend: deepl
  deepl_api_key: <DeepL Pro API key>
  deepl_host: <optional>
  deepl_version: <optional>
  deepl_glossary_ids:
    - f28106eb-0e06-489e-82c6-8215d6f95089
    - 2c6415be-1852-4f54-9e1b-d800463496b4
  deepl_options:
    formality: prefer_less

or via environment variables:

DEEPL_API_KEY=<DeepL Pro API key>
DEEPL_HOST=<optional>
DEEPL_VERSION=<optional>

Yandex Translate

i18n-tasks translate-missing requires a Yandex API key, get it at Yandex.

# config/i18n-tasks.yml
translation:
  backend: yandex
  yandex_api_key: <Yandex API key>

or via environment variable:

YANDEX_API_KEY=<Yandex API key>

OpenAI Translate

i18n-tasks translate-missing requires a OpenAI API key, get it at OpenAI.

# config/i18n-tasks.yml
translation:
  backend: openai
  openai_api_key: <OpenAI API key>
  openai_model: <optional>

or via environment variable:

OPENAI_API_KEY=<OpenAI API key>
OPENAI_MODEL=<optional>

Contextual Rails Parser

There is an experimental feature to parse Rails with more context. i18n-tasks will support:

  • Translations called in before_actions
  • Translations called in nested methods
  • Model.human_attribute_name calls
  • Model.model_name.human calls

Enabled it by adding the scanner in your config/i18n-tasks.yml:

<% I18n::Tasks.add_scanner( 
  'I18n::Tasks::Scanners::PrismScanner',
  only: %w(*.rb)
) %>

To only enable Ruby-scanning and not any Rails support, please add config under the search section:

search:
  prism_visitor: "ruby" # default "rails"

Interactive console

i18n-tasks irb starts an IRB session in i18n-tasks context. Type guide for more information.

Import / export to a CSV spreadsheet

See i18n-tasks wiki: CSV import and export tasks.

Add new tasks

Tasks that come with the gem are defined in lib/i18n/tasks/command/commands. Custom tasks can be added easily, see the examples on the wiki.

Development

  • Install dependencies using bundle install
  • Run tests using bundle exec rspec
  • Install Overcommit by running overcommit --install

Skip Overcommit-hooks

  • SKIP=RuboCop git commit
  • OVERCOMMIT_DISABLE=1 git commit

i18n-tasks's People

Contributors

aleksandrs-ledovskis avatar aried3r avatar bilka2 avatar colinbruce avatar davidwessman avatar deivid-rodriguez avatar dependabot[bot] avatar dmke avatar gargron avatar glebm avatar helenasw avatar lxxxvi avatar michaelbaudino avatar mvz avatar natano avatar nschonni avatar otoyo avatar paulfioravanti avatar pcorpet avatar pglombardo avatar pikachuexe avatar pjaspers avatar pocke avatar prrrnd avatar rap1ds avatar scarroll32 avatar stijnster avatar sylvieed avatar wakematta avatar ypresto 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar

i18n-tasks's Issues

Different locale directory structure

Hi,

Can this work for the following locale directory tree ?

$ tree locales/
locales/
├── de
│   ├── helpers.yml
│   ├── rails_defaults.yml
│   └── views.yml
├── en
│   ├── helpers.yml
│   ├── rails_defaults.yml
│   └── views.yml

Thanks

Error - Google Translate

Every time I use rake i18n:fill:google_translate I keep getting the below error:

rake aborted!
undefined method `encoding' for false:FalseClass

I've entered my API key into the i18n-tasks.yml file. When I run rake i18n:fill:base_value this works without any problems. Have I missed anything in the Google Cloud Console? I've created an application and enabled the Translate API.

Handle non conventional files organization

In one of our projects, each locale has its own directory. For instance the fr locale is stored like this:

config/locales/en
    blocks.en.yml
    devise.en.yml
    en.yml
    formtastic.en.yml
    models.en.yml
    simple_form.en.yml

Obviously I get the No such file or directory - config/locales/fr.yml error when calling rake i18n:missing.

Why is the dependency so hard on locale files ? Isn't there a way to lookup the keys without finding the files this way ?

Thanks :)

Confusing task naming

add_missing task only affects keys in base_locale (unless [locale] is provided)

fill:add_blanks affects all keys by default, essentially calling add_missing for base locale, and adds keys from base to others. it will always run add_missing for base_locale first, regardless whether [locales] are specified

fill:google_traslate and fill:base_value both only affect keys that are present in base but not in others.

This is confusing and something needs to change. Ideas?

(feat) Ignore file

Need an ignore file feature for missing translations (mainly useful to ignore framework translations)

missing task does not find missing translations for the base locale

Hello,

i'm currently using your gem to check my translations and i noticed that it didn't find missing keys for the base locale. I'm on Rails 4 + Ruby 2.

I've the following in a partial:

%h2
  = t "#{prefix}.header"
= t "#{prefix}.explanation"

This results in pattern_key_prefixes method to return an array with two empty strings, which then in turn makes the compile_start_with_re method return the wrong regular expression. I could solve it in my code by always passing the dot with the prefix, but that feels weird.

Adding a check for blank keys in the pattern_key_prefixes method fixes this:

def pattern_key_prefixes
  @pattern_keys_prefixes ||=
  find_source_keys.select { |k| k =~ /\#{.*?}/ || k.ends_with?('.') }.map { |k| k.split(/\.?#/)[0] }.select{|k| !k.blank?}
end

Sorry for the close/reopen-spam and thanks for the helpful tool and all the work already put into it :-)

i18n-tasks merge

Tasks to import (merge) values.

add-missing can then be reduced to:

i18n-tasks missing -fmarshall | i18n-tasks merge -fmarshall

translate-missing to:

i18n-tasks missing -fyaml -p 'TRME %{value}' | i18n-tasks translate -fyaml | i18n-tasks merge -fyaml

Another useful task would be:

i18n-tasks convert -fyaml -tjson <<<DATA

No such file or directory - config/locales/bg.yml

Hi glebm,

When using the task i18n:missing, I'm getting an error:

No such file or directory - config/locales/bg.yml

Obviously I don't have a bg.yml locale file and I don't need nor plan to build one... I'm wondering if I'm missing something obvious when using the task, that leads me to this error?

Thanks in advance and thanks for your Gem.

relative keys in controller actions

@bartoszkopinski

i18n-tasks treats relative keys in controllers as "user_controller.success" and not "users.create.success"

It would be possible to just match the closest method name (almost impossible in general case). PR welcome! You would need to scan back for the method name in the controller from here.

What about those three dashes "---"?

When running i18n-tasks add-missing, there are always three dashes --- at the top of the generated code:

image

Googling for them, they seem to indicate a new Yaml file. But is this necessary? I've never seen them before.

Matching keys with scope syntax

Currently I have such translations in code:

I18n.t(:already_refunded, scope: [:cancellation, :error_messages])

Translation file has:

en:
  cancellation:
    error_messages:
      already_refunded: This has been refunded already.

rake i18n:missing reports:

+--------+------+---------------------------+-----------------+
| Locale | Type | i18n Key                  | Base value (en) |
+--------+------+---------------------------+-----------------+
|   en   |  ✗   | already_refunded          |                 |
+--------+------+---------------------------+-----------------+

while rake i18n:unused reports:

+----------------------------------------------+---------------------------------+
| i18n Key                                     | Base value (en)                 |
+----------------------------------------------+---------------------------------+
| cancellation.error_messages.already_refunded | This has been refunded already. |
+----------------------------------------------+---------------------------------+

How should I set up patterns to match the above? I want to avoid translations like:

I18n.t(:"cancellation.error_messages.already_refunded")

Error when using `rake i18n:missing`

Environment:

  • rails 3.2.15
no implicit conversion of nil into String
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/i18n/tasks.rb:19:in `read'

/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/i18n/tasks.rb:19:in `config'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/i18n/tasks/configuration.rb:11:in `config='
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/i18n/tasks/base_task.rb:33:in `initialize'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/i18n/tasks/reports/base.rb:6:in `new'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/i18n/tasks/reports/base.rb:6:in `initialize'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/tasks/i18n-tasks.rake:74:in `new'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/tasks/i18n-tasks.rake:74:in `i18n_report'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/gems/i18n-tasks-0.2.9/lib/tasks/i18n-tasks.rake:14:in `block (2 levels) in <top (required)>'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/bin/ruby_executable_hooks:15:in `eval'
/Users/PikachuEXE/.rvm/gems/ruby-2.0.0-p353/bin/ruby_executable_hooks:15:in `<main>'
Tasks: TOP => i18n:missing
(See full trace by running task with --trace)

search.scanner

Sometimes there is a need to consider things other than I18n.t calls as usages of translations. At the moment this need is somewhat filled by search.pattern setting, but this is limited to method calls with the same signature as I18n.t.

One example is Vagrant, which uses custom error_key(key, namespace) and error_namespace(namespace) calls. The first one is almost the same as I18n.t in this regard, while the second one is a per-class namespace

Thus, we need a flexible API to allow custom provision of used keys. The API should make it easy to use the existing i18n-tasks infrastructure, and the current scanner should be implemented in terms of this new API.

Sets in key pattern matching

Key pattern matching set support

ignore_unused:
  - simple_form.{placeholders, hints, labels}.*

It would be cool if {...} translated to a group capture, so it can be backreferenced later:

data:
  write:
    - ['{devise, simple_form}.*', 'config/locales/\1.%{locale}.yml']

Spaces within sets are stripped. Delimeter is ,.

We also need a token to match just one i18n namespace, e.g. : (if you have a better idea speak up). An interesting use case is the following configuration:

data:
  write:
    # normalize every namespace to its own file:
    - ['{:}.*', 'config/locales/\1.%{locale}.yml']
    # top level keys, or don't to enforce none
    - 'config/locales/%{locale}.yml'

i18n:missing:blank[locales] returns "undefined local variable or method `args' for main:Object"

Here is the trace:

mion00@mion-HP:~/RubymineProjects/Airesis$ rake i18n:missing:blank[fr] --trace
** Invoke i18n:missing:blank (first_time)
** Invoke i18n:setup (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute i18n:setup
** Execute i18n:missing:blank
rake aborted!
undefined local variable or method `args' for main:Object
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/i18n-tasks-0.2.18/lib/tasks/i18n-tasks.rake:32:in `block (3 levels) in <top (required)>'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:236:in `call'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:236:in `block in execute'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:231:in `each'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:231:in `execute'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:175:in `block in invoke_with_call_chain'
/home/mion00/.rvm/rubies/ruby-1.9.3-p448/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:168:in `invoke_with_call_chain'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/task.rb:161:in `invoke'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:149:in `invoke_task'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:106:in `each'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:106:in `block in top_level'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:115:in `run_with_threads'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:100:in `top_level'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:78:in `block in run'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:165:in `standard_exception_handling'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/lib/rake/application.rb:75:in `run'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/gems/rake-10.1.1/bin/rake:33:in `<top (required)>'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/bin/rake:23:in `load'
/home/mion00/.rvm/gems/ruby-1.9.3-p448/bin/rake:23:in `<main>'
Tasks: TOP => i18n:missing:blank

Disable autowrap after ~85 characters

Thanks for this very, very useful gem. I don't like that it autowraps translations after approx. 85 characters, though.

Is there a way to circumvent this?

Handle exisisting but empty keys foo_bar: ''

Here is a patch:

diff --git a/lib/i18n/tasks/data.rb b/lib/i18n/tasks/data.rb
index a0383b4..163015d 100644
--- a/lib/i18n/tasks/data.rb
+++ b/lib/i18n/tasks/data.rb
@@ -25,7 +25,7 @@ module I18n::Tasks::Data

   # whether the value for key exists in locale (defaults: base_locale)
   def key_value?(key, locale = base_locale)
-    t(key, locale).present?
+    !t(key, locale).nil?
   end

   # write to store, normalizing all data

Sample:

en:
  foo_bar: ''

We need this kind of values in yml

keys which are used as symbols reported as unused?

translations keys which are called as symbols aren't currently recognised as being used when I call rake i18n:unused

examples of keys which aren't found in code:

%h3= t :email_members_in_group, which_group: group.full_name

%h2= t :"help.have_your_say"

if you have suggestions about how to modify the search pattern I would be most grateful. thanks

Error when using `rake i18n:missing` with Rails 4.0.2

When running rake i18n:missing I got the following error:

$ bundle exec rake i18n:missing --trace
** Invoke i18n:missing (first_time)
** Invoke i18n:setup (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute i18n:setup
** Execute i18n:missing
rake aborted!
undefined method `with_indifferent_access' for nil:NilClass
/home/barraq/.gem/ruby/gems/i18n-tasks-0.2.17/lib/i18n/tasks/source_keys.rb:13:in `scanner'
/home/barraq/.gem/ruby/gems/i18n-tasks-0.2.17/lib/i18n/tasks/source_keys.rb:8:in `find_source_keys'
/home/barraq/.gem/ruby/gems/i18n-tasks-0.2.17/lib/i18n/tasks/missing_keys.rb:4:in `keys_not_in_base'
/home/barraq/.gem/ruby/gems/i18n-tasks-0.2.17/lib/i18n/tasks/untranslated_keys.rb:16:in `keys_not_in_base_info'
/home/barraq/.gem/ruby/gems/i18n-tasks-0.2.17/lib/i18n/tasks/untranslated_keys.rb:11:in `untranslated_keys'
/home/barraq/.gem/ruby/gems/i18n-tasks-0.2.17/lib/tasks/i18n-tasks.rake:16:in `block (2 levels) in '
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:236:in `call'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:236:in `block in execute'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:231:in `each'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:231:in `execute'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:175:in `block in invoke_with_call_chain'
/usr/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:168:in `invoke_with_call_chain'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/task.rb:161:in `invoke'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:149:in `invoke_task'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:106:in `each'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:106:in `block in top_level'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:115:in `run_with_threads'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:100:in `top_level'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:78:in `block in run'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:165:in `standard_exception_handling'
/home/barraq/.gem/ruby/gems/rake-10.1.1/lib/rake/application.rb:75:in `run'
/home/barraq/.gem/ruby/gems/rake-10.1.1/bin/rake:33:in `'
/home/barraq/.gem/ruby/bin/rake:23:in `load'
/home/barraq/.gem/ruby/bin/rake:23:in `'
Tasks: TOP => i18n:missing

I am using Rails 4.0.2.

Note: my locales are organized as described in http://guides.rubyonrails.org/i18n.html#organization-of-locale-files, namely I have a %{locale}.yml file for all my views etc.

add-missing placeholder base value

It would be really useful to simply use the base's string as the temporary translation for missing keys. This way I can simply translate the base's string and don't have to invent a new one (or manually go back to the base's language file and take a look there).

Also, it would be nice if the gem added some unique prefix to newly added translations (e.g. TRANSLATE:) so I could search all translations for it and translate one by one. I know that I can do a git diff, but that's just not as handy.

Check for translation of model names and attributes

I have written two specs which check for untranslated model names and attributes. Their output looks like this:

image

Because you don't want to always translated every model's name and attributes, I added two keys to the i18n-tasks.yml file where one can specify exceptions:

ignore_untranslated:
  model_names:
    - role
  model_attributes:
    user:
      - encrypted_password
      - reset_password_token
      - reset_password_sent_at
      - remember_created_at
      - unconfirmed_email
      - confirmation_token
      - confirmation_sent_at
      - unlock_token
      - sign_in_count

The code is not refactored yet, so I won't post it here at this time. But would you be interested in a wiki page or something about this topic?

grep exclude pattern

I have a bunch of Javascript assets (app/assets/javascripts/*.js*) which make use of t() calls, and will therefore be found by grep, although they're not i18n related.

It would be nice to be able to configure grep via its --exclude=GLOB paramter from the config/i18n-tasks.yml like so:

grep:
  exclude:
    - '*.js*'
    - '*.(le,sa,c)ss'

and later in I18n::Tasks::BaseTask#find_source_keys extend the run_command 'grep' ... call.

What do you think?

Using "default" as base_value

Hello,

i want to use the "default" value as base_value.

Example:
in my code:

<%= t '.invite_header', default: 'Leitstellenspiel weiterempfehlen' %>

The german file should look like:
referrals:
index:
invite_header: Leitstellenspiel weiterempfehlen

How can i do this?

best regards,
Sebastian

Version 0.3.7 breaks on Rails < 4

After updating to version 0.3.7 we get this error when using rake tasks:

$ rake -T
rake aborted!
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/i18n-tasks-0.3.7/lib/i18n/tasks/data/locale_tree.rb:7: syntax error, unexpected '.'
            class.__send__(:tree_data, *args, &...
                  ^
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/i18n-tasks-0.3.7/lib/i18n/tasks/data/locale_tree.rb:9: syntax error, unexpected '.'
            if class.nil?                         ...
                     ^
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/i18n-tasks-0.3.7/lib/i18n/tasks/data/locale_tree.rb:14: syntax error, unexpected keyword_end, expecting $end
          end                                                 # end
             ^
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/activesupport-3.2.17/lib/active_support/core_ext/module/delegation.rb:139:in `module_eval'
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/activesupport-3.2.17/lib/active_support/core_ext/module/delegation.rb:139:in `block in delegate'
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/activesupport-3.2.17/lib/active_support/core_ext/module/delegation.rb:125:in `each'
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/activesupport-3.2.17/lib/active_support/core_ext/module/delegation.rb:125:in `delegate'
/Users/fred/.rvm/gems/ruby-1.9.3-p484@p9set/gems/i18n-tasks-0.3.7/lib/i18n/tasks/data/locale_tree.rb:7:in `'

... (long trace)

We are using Rake 10.1.1 and Rails 3.2.17. Downgrading i18n-tasks to 0.3.6 solves this issue.

Better RSpec integration

Your RSpec integration doesn't provide a lot of information when a spec fails.

image

I refactored both specs like this:

describe 'I18n translation'  do
  before { @i18n = I18n::Tasks::BaseTask.new }

  it "doesn't have any missing keys" do
    count = @i18n.missing_keys.count
    fail "There are #{count} missing i18n keys! Run 'i18n-tasks missing' for more details." if count > 0
  end

  it "doesn't have any unused keys" do
    count = @i18n.unused_keys.count
    fail "There are #{count} unused i18n keys! Run 'i18n-tasks unused' for more details." if count > 0
  end
end

I think the generated output is much more useful:

image

Would you accept a PR with this?

No relative key root detected for "." Runtime Error

Running i18n-tasks missing gets me this:

ezuk@ezuk-mm-vm:~/mimi$ i18n-tasks missing
/home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/relative_keys.rb:11:in `absolutize_key': No relative key root detected for "." at /home/ezuk/mimi/app/manifests/deb/powermta_4.0b3-201103151827_amd64.deb. Please set relative_roots in config/i18n-tasks.yml (currently set to ["app/views"]) (RuntimeError)
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/pattern_scanner.rb:36:in `match_to_key'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb:23:in `match_to_key'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/pattern_scanner.rb:13:in `block in scan_file'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/pattern_scanner.rb:11:in `scan'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/pattern_scanner.rb:11:in `scan_file'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/base_scanner.rb:32:in `block in keys'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/base_scanner.rb:74:in `block in traverse_files'
        from /home/ezuk/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/find.rb:47:in `block (2 levels) in find'
        from /home/ezuk/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/find.rb:46:in `catch'
        from /home/ezuk/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/find.rb:46:in `block in find'
        from /home/ezuk/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/find.rb:42:in `each'
        from /home/ezuk/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/find.rb:42:in `find'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/base_scanner.rb:66:in `traverse_files'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/scanners/base_scanner.rb:32:in `keys'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/used_keys.rb:19:in `used_keys'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/missing_keys.rb:31:in `keys_missing_from_base'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/missing_keys.rb:15:in `missing_keys'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/missing_keys.rb:11:in `block in missing_keys'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/missing_keys.rb:11:in `map'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/missing_keys.rb:11:in `missing_keys'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/lib/i18n/tasks/commands.rb:18:in `block in <class:Commands>'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/gems/i18n-tasks-0.3.11/bin/i18n-tasks:45:in `<top (required)>'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/bin/i18n-tasks:23:in `load'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/bin/i18n-tasks:23:in `<main>'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/bin/ruby_executable_hooks:15:in `eval'
        from /home/ezuk/.rvm/gems/ruby-2.1.1@madmimi/bin/ruby_executable_hooks:15:in `<main>'

Any idea why? Thank you!

undefined method `strip' for #<Array:0x007f7faa5bde58>

Another exception on i18n:unused

rake aborted!
undefined method `strip' for #<Array:0x007f7faa5bde58>
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:18:in `block in unused'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:18:in `each'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:18:in `unused'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/tasks/i18n-tasks.rake:26:in `block (2 levels) in <top (required)>'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:236:in `call'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:236:in `block in execute'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:231:in `each'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:231:in `execute'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:175:in `block in invoke_with_call_chain'
/Users/ruud/.rvm/rubies/ruby-1.9.3-p448-railsexpress/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:168:in `invoke_with_call_chain'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:161:in `invoke'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:149:in `invoke_task'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:106:in `each'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:106:in `block in top_level'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:115:in `run_with_threads'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:100:in `top_level'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:78:in `block in run'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:165:in `standard_exception_handling'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:75:in `run'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/bin/rake:33:in `<top (required)>'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/rake:23:in `load'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/rake:23:in `<main>'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/ruby_noexec_wrapper:14:in `eval'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/ruby_noexec_wrapper:14:in `<main>'
Tasks: TOP => i18n:unused

uninitialized constant Yaml (NameError)

Whenever I try to run i18n-tasks missing or i18n-tasks unused I get:

/Users/Username/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.0.4/lib/active_support/inflector/methods.rb:228:in `const_get': uninitialized constant Yaml (NameError)

My versions:

0.3.9
rbenv 0.4.0-95-gf71e227
ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin13.0]
Rails 4.0.4
OS X Mavericks

Is this a known issue to you?

"missing" task does not consider parameters (ruby 1.9)

Nice job with the new release!

However, when running i18n-tasks missing -l it-IT, I get a list of all the missing values in all locales, while I am looking for only the italian ones.

mion00@mion-HP:~/RubymineProjects/Airesis$ i18n-tasks -v 0.3.3
mion00@mion-HP:~/RubymineProjects/Airesis$ ruby -v ruby 1.9.3p448 (2013-06-27 revision 41675) [x86_64-linux]

Crawl controllers?

At the moment using rake i18n:missing won't report all the strings created in controllers using i18n.translate, only the ones using t are reported

translate-missing error message when Google Translate billing is disabled

Hi!
Thanks for awesome gem!
Please help me to find out what am I doing wrong? May be this is a bug?

$ rake i18n:fill:google_translate
rake aborted!
undefined method `each_with_index' for nil:NilClass

/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/google_translation.rb:25:in `fetch_google_translations'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/google_translation.rb:16:in `block in google_translate'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/google_translation.rb:15:in `each'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/google_translation.rb:15:in `map'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/google_translation.rb:15:in `google_translate'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/fill_tasks.rb:31:in `block in fill_with_google_translate!'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/fill_tasks.rb:24:in `each'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/i18n/tasks/fill_tasks.rb:24:in `fill_with_google_translate!'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bundler/gems/i18n-tasks-44c60b28edeb/lib/tasks/i18n-tasks.rake:89:in `block (3 levels) in <top (required)>'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bin/ruby_executable_hooks:15:in `eval'
/Users/exr/.rvm/gems/ruby-2.0.0-p353@project/bin/ruby_executable_hooks:15:in `<main>'

API key set up in config (config/I18n-tasks.yml):

translation:
  api_key: *cutted*

Key matching syntax

We need more powerful key pattern syntax for everywhere in the configuration. e.g. devise.*.text.

PRs welcome.
See also #18

Optionally retain custom structure of locale files

If an application exceeds a certain amount of complexity, dealing with I18n keys within just a few files becomes quite unhandy. For that reason the official Rails guide recommends a way to organize the locale files for an affected application (http://guides.rubyonrails.org/i18n.html#organization-of-locale-files).

I played around a bit (like trial-and-error) with the data-write settings within the config file. But to me it seems like it isn't part of the scope yet to just read the original files, decide which keys are missing in the target locale and create the corresponding files and/or keys on the respective folder level.

Is there any possibility to achieve this? Do you think that this is a use case worth considering in the i18n-tasks workflow?

To be clear: Reading the data from this custom structure is easily done - obviously.

# config/i18n-tasks.yml
# ...
data:
  read:
    - 'config/locales/defaults/%{locale}.yml'
    - 'config/locales/defaults/%{locale}.*.yml'
    - 'config/locales/models/*/%{locale}.yml'
    - 'config/locales/views/*/%{locale}.yml'
# ...

Confusing README for config

I see this in README:

# config/i18n-tasks.yml
base_locale: en
locales: [es, fr]

Does locales need to include base_locale?

Relative roots are not correctly detected

I found out that despite using keys in a file under myapp/lib/validators/name_format_validator.rb i18n-tasks reported these keys as unused. I looked into my config file and saw this:

relative_roots:
  - app/views
  - app/controllers
  - app/helpers
  - app/models

So I added - lib, but i18n-tasks still reported false positives. I also tried - lib/ and restarted my terminal, but that did not change. What else must I do additionally to that?

undefined method `strip' for ["one", "other"]:Array

Nice gem but not yet working for me though.

** Invoke i18n:missing (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute i18n:missing
Missing keys and translations (4266)
Legend:  key missing,  translation blank, = value equal to base locale; value in base locale
de          messages.view_availability                                                                                    Beschikbaarheid
rake aborted!
undefined method `strip' for ["one", "other"]:Array
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/activesupport-3.2.11/lib/active_support/core_ext/object/try.rb:36:in `try'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:31:in `print_missing_translation'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:12:in `block in missing'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:12:in `each'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/i18n/tasks/output/terminal.rb:12:in `missing'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/gems/i18n-tasks-0.1.2/lib/tasks/i18n-tasks.rake:21:in `block (2 levels) in <top (required)>'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:236:in `call'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:236:in `block in execute'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:231:in `each'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:231:in `execute'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:175:in `block in invoke_with_call_chain'
/Users/ruud/.rvm/rubies/ruby-1.9.3-p448-railsexpress/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:168:in `invoke_with_call_chain'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/task.rb:161:in `invoke'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:149:in `invoke_task'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:106:in `each'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:106:in `block in top_level'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:115:in `run_with_threads'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:100:in `top_level'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:78:in `block in run'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:165:in `standard_exception_handling'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/lib/rake/application.rb:75:in `run'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress@global/gems/rake-10.1.0/bin/rake:33:in `<top (required)>'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/rake:23:in `load'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/rake:23:in `<main>'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/ruby_noexec_wrapper:14:in `eval'
/Users/ruud/.rvm/gems/ruby-1.9.3-p448-railsexpress/bin/ruby_noexec_wrapper:14:in `<main>'
Tasks: TOP => i18n:missing

ability to change locale_file_path of prefill

first at all: nice work! helps alot ;)

following scenario:

my i18n-tasks.yml

data:
  paths:
    - 'config/locales/mystuff/%{locale}.yml'

now, running i18n:prefill will use locale_file_path and change the files in config/locales/

config/locales/
├── de.yml
├── en.yml
├── mystuff
│   ├── bg.yml
│   ├── de.yml
│   └── en.yml

relative keys

Hey,

i just tried your tool and it works great!

i have one issue with relative keys like:

 = t(".personal_contact")

which i defined in file app/views/accounts/show.html.haml and it will be listed by the rake i18n:unusedtask.

a little bit tricky to match that key...

rake i18n:missing does not work on rails4

rake i18n:missing --trace
** Invoke i18n:missing (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute i18n:missing
rake aborted!
undefined method split' for nil:NilClass /Users/tomo/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.1.0/lib/i18n/tasks/base_task.rb:25:infind_source_keys'

Importing tasks

It would be cool if i18n-tasks could report from various formats (e.g. xlsx) to make outsourcing translation easier.

comparison of Array with Array failed

user@Machine:~/path$ i18n-tasks add-missing
/usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/locale_tree.rb:72:in `sort': comparison of Array with Array failed (ArgumentError)
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/locale_tree.rb:72:in `list_to_tree_data'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/locale_tree.rb:63:in `to_tree_data'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/locale_tree.rb:55:in `to_tree_data'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/locale_tree.rb:9:in `initialize'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/router.rb:35:in `new'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/router.rb:35:in `block in route_values'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/router.rb:34:in `each'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/router.rb:34:in `route_values'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data/file_system_base.rb:54:in `set'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data.rb:75:in `update_locale_data'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data.rb:50:in `block in update_data'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data.rb:50:in `each'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/data.rb:50:in `update_data'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/fill_tasks.rb:7:in `fill_missing_value'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/lib/i18n/tasks/commands.rb:57:in `block in <class:Commands>'
    from /usr/local/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.3.10/bin/i18n-tasks:45:in `<top (required)>'
    from /usr/local/bin/i18n-tasks:23:in `load'
    from /usr/local/bin/i18n-tasks:23:in `<main>'

unused and missing commands work well though.

user@Machine:~/path$ rails -v
Rails 4.0.3
user@Machine:~/path$ ruby -v
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]

Error running i18n-tasks add-missing base

I am receiving this error when trying to add missing messages to the base locale

$i18n-tasks  add-missing base
/Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:79:in `[]=': string not matched (IndexError)
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:79:in `block in list_to_tree_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:74:in `each'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:74:in `list_to_tree_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:63:in `to_tree_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:55:in `to_tree_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data/locale_tree.rb:13:in `merge'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data.rb:75:in `update_locale_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data.rb:50:in `block in update_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data.rb:50:in `each'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/data.rb:50:in `update_data'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/fill_tasks.rb:7:in `fill_missing_value'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/lib/i18n/tasks/commands.rb:57:in `block in <class:Commands>'
    from /Users/tvoet/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/i18n-tasks-0.3.11/bin/i18n-tasks:45:in `<top (required)>'
    from /Users/tvoet/.rbenv/versions/2.1.0/bin/i18n-tasks:23:in `load'
    from /Users/tvoet/.rbenv/versions/2.1.0/bin/i18n-tasks:23:in `<main>'

this is my config config/i18n-tasks.yml

base_locale: en
locales: [en, fr]
data:
  yaml:
    write:
      line_width: -1
  # a list of file globs to read from per-locale
  read:
    # read from namespaced files, e.g. simple_form.en.yml
    - 'config/locales/*.%{locale}.yml'
    # read from a gem (config is parsed with ERB first, then YAML)
    #- "<%= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
    # default
    - 'config/locales/%{locale}.yml'
  # a list of {key pattern => file} routes, matched top to bottom
  write:
    # write models.* and views.* keys to the respective files
    - ['{models,views}.*', 'config/locales/\1.%{locale}.yml']
    # or, write every top-level key namespace to its own file
    #- ['{:}.*', 'config/locales/\1.%{locale}.yml']
    # default, sugar for ['*', path]
    - 'config/locales/%{locale}.yml'

translation:
  api_key: <my_api_key>

invalid byte sequence in US-ASCII

When I run rake i18n:unused, it throws 'invalid byte sequence in US-ASCII' error, and it seems no way to guess from the trace where this error is encountered.

$ rake i18n:unused
rake aborted!
rake aborted!
invalid byte sequence in US-ASCII
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/trace_output.rb:16:in `block in trace_on'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/trace_output.rb:14:in `map'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/trace_output.rb:14:in `trace_on'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:328:in `trace'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:183:in `display_error_message'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:169:in `rescue in standard_exception_handling'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:159:in `standard_exception_handling'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:88:in `load_rakefile'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:72:in `block in run'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:160:in `standard_exception_handling'
/home/den/.rvm/gems/ruby-1.9.3-p374/gems/rake-10.0.4/lib/rake/application.rb:70:in `run'
/home/den/.rvm/gems/ruby-1.9.3-p374/bin/ruby_noexec_wrapper:14:in `eval'
/home/den/.rvm/gems/ruby-1.9.3-p374/bin/ruby_noexec_wrapper:14:in `<main>'
(See full trace by running task with --trace)

ruby-1.9.3-p374, Rails 3.2.13.

"normalize" destroys stuff like &defaults

I often use stuff like the following:

de:
  activerecord:
    attributes:
      defaults: &defaults
        created_at: 'Erstellt am'
        updated_at: 'Aktualisiert am'
      user:
        <<: *defaults
        current_password: Aktuelles Passwort

Sadly, when doing i18n-tasks normalize, these are "flattened". Is there any way to circumvent this?

Configurable relative key roots

For mobile views I will use

prepend_view_path Rails.root + 'app' + 'views_mobile'

and reference a key using the t('.') syntax.

The i18n:missing task will report these keys as missing. I could scope the keys under app.views_mobile but I would rather not, so I choose to add them to ignore_missing in the config file.

[feature] Write only missing keys to file

I have a big internationalization file from a source I don't control. The source has the base locale and I have to maintain my translation file. I would be nice to do that incrementally. Instead of delving into the whole file, I'd just create another one and translate the missing keys.

The motivation for this also came because i18n-tasks is finding trouble when writing keys with single quotes inside.
unusual_key: This 'name' has single quotes

When doing this I get results like:
unusual_key: ! 'This ''name'' has single quotes'

That is being interpreted as invalid YAML.

i18n:missing - undefined method `keys' for nil:NilClass

We have a problem with i18n-tasks v0.2.21 in the "vagrant-parallels" project :

$ rake i18n:missing --trace
** Invoke i18n:missing (first_time)
** Invoke i18n:setup (first_time)
** Execute i18n:setup
** Execute i18n:missing
rake aborted!
undefined method `keys' for nil:NilClass
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.2.21/lib/i18n/tasks/key_group.rb:58:in `merge'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.2.21/lib/i18n/tasks/missing_keys.rb:16:in `missing_keys'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/i18n-tasks-0.2.21/lib/tasks/i18n-tasks.rake:15:in `block (2 levels) in <top (required)>'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:236:in `call'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:236:in `block in execute'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:231:in `each'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:231:in `execute'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:175:in `block in invoke_with_call_chain'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:168:in `invoke_with_call_chain'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/task.rb:161:in `invoke'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:149:in `invoke_task'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:106:in `each'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:106:in `block in top_level'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:115:in `run_with_threads'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:100:in `top_level'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:78:in `block in run'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:165:in `standard_exception_handling'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/lib/rake/application.rb:75:in `run'
/Users/legal/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/rake-10.1.1/bin/rake:33:in `<top (required)>'
/Users/legal/.rbenv/versions/2.0.0-p353/bin/rake:23:in `load'
/Users/legal/.rbenv/versions/2.0.0-p353/bin/rake:23:in `<main>'
Tasks: TOP => i18n:missing

It was reproduced in our fix-i18n-tasks branch:
https://github.com/Parallels/vagrant-parallels/tree/fix-i18n-tasks

Seem like it is a i18n-tasks bug and we really worry about that because our Travis CI builds are broken now.

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.