Giter VIP home page Giter VIP logo

trans's Introduction

Trans

Tests Hex.pm

Trans provides a way to manage and query translations embedded into schemas and removes the necessity of maintaining extra tables only for translation storage. It is inspired by the great hstore translate gem for Ruby.

Trans is published on hex.pm and the documentation is also available online. Source code is available in this same repository under the Apache2 License.

On April 17th, 2017, Trans was featured in HackerNoon

Optional Requirements

Having Ecto SQL and Postgrex in your application will allow you to use the Trans.QueryBuilder component to generate database queries based on translated data. You can still use the Trans.Translator component without those dependencies though.

  • Ecto SQL 3.0 or higher
  • PostgreSQL 9.4 or higher (since Trans leverages the JSONB datatype)

Why Trans?

The traditional approach to content internationalization consists on using an additional table for each translatable schema. This table works only as a storage for the original schema translations. For example, we may have a posts and a posts_translations tables.

This approach has a few disadvantages:

  • It complicates the database schema because it creates extra tables that are coupled to the "main" ones.
  • It makes migrations and schemas more complicated, since we always have to keep the two tables in sync.
  • It requires constant JOINs in order to filter or fetch records along with their translations.

The approach used by Trans is based on modern RDBMSs support for unstructured datatypes. Instead of storing the translations in a different table, each translatable schema has an extra column that contains all of its translations. This approach drastically reduces the number of required JOINs when filtering or fetching records.

Trans is lightweight and modularized. The Trans module provides metadata that is used by the Trans.Translator and Trans.QueryBuilder modules, which implement the main functionality of this library.

Setup and Quickstart

Let's say that we have an Article schema that contains texts in English and we want to translate it to other languages.

defmodule MyApp.Article do
  use Ecto.Schema

  schema "articles" do
    field :title, :string
    field :body, :string
  end
end

The first step would be to add a new JSONB column to the table so we can store the translations in it.

defmodule MyApp.Repo.Migrations.AddTranslationsToArticles do
  use Ecto.Migration

  def change do
    alter table(:articles) do
      add :translations, :map
    end
  end
end

Once we have the new database column, we can update the Article schema to include the translations.

defmodule MyApp.Article do
  use Ecto.Schema
  use Trans, translates: [:title, :body], default_locale: :en

  schema "articles" do
    field :title, :string
    field :body, :string

    # This generates a MyApp.Article.Translations schema with a
    # MyApp.Article.Translations.Fields for :es and :fr
    translations [:es, :fr]
  end
end

After doing this we can leverage the Trans.Translator and Trans.QueryBuilder modules to fetch and query translations from the database.

The translation storage can be done using normal Ecto.Changeset functions just like it would be done for any other fields or associations.

defmodule MyApp.Article do
  def changeset(article, attrs \\ %{}) do
    article
    |> cast(attrs, [:title, :body])
    |> validate_required([:title, :body])
    |> cast_embed(:translations, with: &cast_translations/2)
  end

  defp cast_translations(translations, attrs \\ %{}) do
    translations
    |> cast(attrs, [])
    |> cast_embed(:es)
    |> cast_embed(:fr)
  end
end

# Then, anywhere in your code:
changeset = MyApp.Article.changeset(article, %{
  translations: %{
    es: %{title: "title ES", body: "body ES"},
    fr: %{title: "title FR", body: "body FR"}
  }
})

Customizing the translation container

By default Trans looks for a translations field that contains the translations. This is known as the "translation container".

You can override the default translation container passing the container option to Trans. In the following example the translations will be stored in the transcriptions field.

defmodule MyApp.Article do
  use Ecto.Schema
  use Trans, translates: [:title, :body], default_locale: :en, container: :transcriptions

  schema "articles" do
    field :title, :string
    field :body, :strings
    translations [:es, :fr]
  end
end

Customizing the translation schemas

If you want to use your own translation module you can simply pass the build_field_schema: false option when using the translations macro.

defmodule MyApp.Article do
  use Ecto.Schema
  use Trans, translates: [:title, :body], default_locale: :en

  defmodule Translations.Fields do
    use Ecto.Schema

    embedded_schema do
      field :title, :string
      field :body, :string
    end
  end

  schema "articles" do
    field :title, :string
    field :body, :string

    translations [:es, :fr], build_field_schema: false
  end
end

Is Trans dead?

Trans has a slow release cadence, but that does not mean that it is dead. Trans can be considered as "done" in the sense that it does one thing and does it well.

New releases will happen when there are bugs or new changes. If the last release is from a long time ago you should take this as a sign of stability and maturity, not as a sign of abandonment.

trans's People

Contributors

0urobor0s avatar assimelha avatar crbelaus avatar goravbhootra avatar josevalim avatar kianmeng avatar kipcole9 avatar krns avatar philwaldmann avatar remi avatar seb3s avatar sfusato 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

trans's Issues

Make Ecto an optional dependency

The Trans.Translator module can be used on ordinary Structs or Maps without having to be mapped as Ecto.Schemas.
Ecto and Postgrex should be keep as optional dependencies that are only required for using the functions of the Trans.QueryBuilder module.

The documentation should also be updated to reflect this.

Review dependencies

The dependencies should be reviewed, organized and reduced when possible.

The dependency earmark is not neccesary, since it is already provided by ex_doc. ex_doc itself should be required only for :doc instead of :dev.

Make Trans.QueryBuilder.with_translation_ilike/5 private

The function Trans.QueryBuilder.with_translation_ilike/5 has public access. This function should not be directly accessed and instead should be used by calling the Trans.QueryBuilder.with_translation/5 function.

This change will produce a modification of the Trans API, so I have to think in which version it should be included.

Update test schemas

Currently the tests only use the Article schema. We could add a Comment schema that belongs to an Article. Each Article has many Comments.

This would allow better testing and could be useful when implementing the QueryBuilder redesign detailed in #28

Phoenix demo project?

Hi!

I tried looking in your repos, but I couldn't find any demo on how to use Trans with Phoenix forms, and I don't know how to achieve a form that returns the data structure Trans requires.

Can we have a demo on how to use Trans with Phoenix?

Thanks!!

Trans.Translator.translate/2 does not work as expected

I have the following schema:

embeds_one :translations, Translations, on_replace: :update, primary_key: false do
      embeds_one :fr, XXX.Translation, on_replace: :update
      embeds_one :es, XXX.Translation, on_replace: :update
    end

Then the following changeset

|> cast_embed(:translations, with: {__MODULE__, :update_translations, []})

...

def update_translations(changeset, attrs) do
    changeset
    |> cast(attrs, [])
    |> cast_embed(:fr)
    |> cast_embed(:es)
  end

finally the translation embed changeset is as follow:

  def changeset(translation, attrs) do
    translation
    |> cast(attrs, [:name, :description])
    |> validate_required(:name)
  end

(side note, I think the above should be better documented as it might not be straightforward for someone who has never worked with embed schema)

When using the above, I can add and edit translations as expected. Yet if I do not provide an :es translation and then use the
Trans.Translator.translate(Struct, :es) function, instead of getting the default value for :name and :description in my initial schema I'm getting nil

If instead I call Trans.Translator.translate(Struct, :name, :es) I'm observing the correct behaviour (ie the value returned is the one of the original field).

I was expecting translate/2 to work similarly to translate/3 but on the whole struct level (automatically replacing the fields based on the translatable: list provided when declaring use Trans at the top of my main module...)

Reintroduce custom translation containers

With PR #31 Trans lost de capability of using a custom translation container and instead was always trying to use the field named translations to store and look for the translations.

While having the translations in a field named translations will be the most common case, there may also exist certain cases in which translations must be stored in a different field. This issue proposes the following changes to gain this functionality back:

  • Allow users to specify the desired translation container when using Trans in their modules. If ommited the translation container field name will default to translations.
  • Generate a __trans__(:container) function which returns the name of the translation container for a given module.
  • Update Trans.Translator.translate and Trans.Translator.QueryBuilder to use this function for retrieving the name of the translator container field.

Issue with cyrillic search

Hi, I have tried to add translations for Serbian and Russian language and case insensitive search does not work.

Any idea why that might be a case?

Redesing QueryBuilder component

Although the Trans.QueryBuilder module is one of the main features of Trans, it has not been modified since it was originally written.
There are multiple issues with the current design of the Trans.QueryBuilder module, which could be benevolently described as naive (if not plain wrong).

The Trans.QueryBuilder module can not be used when creating a query with Ecto.Query.from/2
Since the functions provided by the Trans.QueryBuilder must receive an Ecto.Queryable, they must receive a query or a queryable object and can not be used at the moment of building a query.

The Trans.QueryBuilder module does not work properly with queries containing joins or conditions across multiple schemas*
The functions provided by the Trans.QueryBuilder module apply the relevant conditions only to the first queryable element of the query. This means that if we create a query for Articles joining with Comments and want to apply a condition on Comments translations, the condition would be instead applied over the Articles

TODO add examples of those problems
TODO propose solutions to fix those problems

Trans.Translator.translate/4 should honor default translation container

At the moment, the function Trans.Translator.translate/4 requires to be passed the translation container name if the Struct uses one with a different name than translations.

Usually, this information is specified when the schema uses the Trans module like this:

      defmodule Article do
        use Ecto.Schema
        use Trans, defaults: [container: :article_translations],
          translates: [:title, :body]

        schema "articles" do
          field :title, :string
          field :body, :string
          field :article_translations, :map
        end
      end

The function Trans.Translator.translate/4 should be able to fetch this information automatically.

accept `locale` also as string

I'm usually using Trans together with Gettext.get_locale().

Gettext.get_locale() returns the locale as a string (eg. "es"), while Trans.Translator.translate/3 only accepts an atom:
Trans.Translator.translate(article, :title, :es)

It would be nice to be able to use it as below directly:
Trans.Translator.translate(article, :title, Gettext.get_locale())

Possible issue on Readme file

Hi, I've found this library and I will be using soon. I was checking the Readme and found this code:

update table(:articles) do
      add :translations, :map
end 

Shouldn't be alter table instead update table ?

Specify translatable attributes

When adding convencience functions to a translatable model (by using Trans module) it would be nice to explictly specify which attributes are translatable, and enforce the existence of those attributes in the changeset.

I am thinking about something like this (observe the fields opt being passed to use Trans)

defmodule Trans.Article do
  use Ecto.Schema
  use Trans, fields: [:title, :body], container: :translations

  import Ecto.Changeset

  ...
end

About the validation, I am thinking about adding a new function to the model that checks if provided translations match the specified fields passed before. This function should be called explicitly on the desired changeset.

Additional translation method without fallback

Additional to

Trans.Translator.translate(article, :title, :de) -> fallback, returns the default

something like

Trans.Translator.translate!(article, :title, :de) -> should raise an error

would be useful.

PS: Nice library, thank you. Already was in love with the RoR version :)

Issues when trying to order by a translated field

I'm trying to order a query result by a specific field in a language, but I don't see any examples and my attempt to doing doesn't work.

I'm trying something like this:
order_by(asc: translated(FoodItem, :name, ^language))

And the error on compile is the following,

== Compilation error on file lib/ketchup/food_item/food_item_helper.ex ==
** (ArgumentError) The locale code must be either an atom or a string
    lib/trans/query_builder.ex:104: Trans.QueryBuilder.locale/1
    expanding macro: Trans.QueryBuilder.translated/3

language is always an atom, but I don't understand how is checking in compile time instead in runtime.

I managed to get this work by using fragment,

|> order_by(asc: fragment("translations -> ? -> 'name'", ^Atom.to_string(language)))

Support Ecto 2.0

Ecto 2.0 has been released some time ago, and Trans should be updated to support it.

Remove helper functions when useing Trans

If a certain module uses Trans, two new functions will be added to that module:

  • with_translations/3
  • with_translation/5

Those functions are simply wrappers over the functions provided by Trans.QueryBuilder. The wrapper functions simply read the default translation configurations for the module and eliminate the necessity for repeating over and over again with each Trans.QueryBuilder call.

We could use a different approach though. When using Trans from our module, we could set up some __special__ functions returning the translation configurations. Calls to Trans.QueryBuilder could check the existence of those functions and automatically call them if they exist.

Integration with ElasticSearch

Hi, first thanks for the project, looks amaizing. I'm considering using in a pet project. In this pet project I would also like to try ElasticSearch.

My question is if someone has indexed in elastic search translated fields with trans.

Thanks and sorry if it's already covered in docs, I did not see nothing related.

Improve documentation

The organization of the docs should be improved. Some examples could be Ecto, Plug or Decimal

The main areas to improve are:

  • Explain clearly what Trans does.
  • Improve documentation organization.
  • Provide better examples.
  • Make use of doctests.

Use GitHub actions for CI

Currently we are using CircleCI. The last build didn't pass because of changes on their part. With GitHub actions we don't need to depend on external services anymore.

Returning a translated record from the DB

πŸ‘‹πŸΌ Hi there, I just started using this great library (thanks for sharing it πŸ™ŒπŸΌ ), and I was wondering what would be the best approach to return a translated record from the DB. From what I've read in the main version, translated_as returns a translated field that Ecto can load into a struct, so I'm doing something like the following:

  def with_translated_name(query, locale) do
    from c in query,
      select_merge: %{name: translated_as(Car, c.name, locale)}
  end

Is this how it is intended to be used, or is there a better way of doing it without the select_merge?
I haven't found any examples in the tests.

Thanks in advance!

[Proposal] Define the default locale and support locale fallbacks

I wonder if you would consider PRs for the following two proposals?

I am the author of the ex_cldr libraries. With the recent work by @maennchen, we now have good integration between gettext and ex_cldr. My next step is to make it really easy for ex_cldr users to work with trans. While its definitely not hard to do today, there are a couple of things that would help.

Overall I think this would create a very good partnership between Trans, Gettext and ExCldr which can only make Elixir stronger as a platform for localised applications and lower the barrier to entry for making more applications localised.

;TLDR

  • I propose adding a :default_locale option to use Trans to define what the default locale is
  • I propose adding support for locale fallback chains to Trans.Translator and Trans.QueryBuilder
  • I am fine to do the work and submit PRs if there is support for the proposal

Cldr integration with schema definitions

I have drafted a prototype implementation of how a Trans schema might leverage ex_cldr. This prototype looks like the following, using the example from the README. Basically, by introspecting the ex_cldr backend we can determine the available locales and generate the schema definition required. Overall I think it also looks quite clean and easily understood.

defmodule MyApp.Article do
  use Ecto.Schema
  use Trans, translates: [:title, :body], default_locale: :en
  use MyApp.Cldr.Trans

  schema "articles" do
    field :title, :string
    field :body, :string

    # Configures all locales known to MyApp.Cldr
    # The usual options can also be specified
    translations :translations, MyApp.Article.Translation
  end
end

Allow defining the default locale

While there is the concept of a default locale in trans, there is no identification of what that locale is. Why is that useful? One use case relates to the example above: we would not want to configure embedded schemas for the default locale so we need to know what it is! Another reason is that if specifying the default locale with Trans.QueryBuilder then the translations do not need to be retrieved or accessed. This would would also aid identifying what translations are available for a schema. Lastly, related to the next proposal, is the ability to support locale fallback chains. To do so requires knowing what the locale of the base column is.

What would this look like? I propose:

use Trans, translates: [:title, :body], default_locale: :en 

Allow fallback chains in Trans.Translator and Trans.QueryBuilder

In many localised applications, the translations and localisation may be sparse. For example, if my application is configured to support en-AU, en-GB, en-CA and en-US then many localisations could be common (where terms, spelling and colloquialisms are the same for example). In such a case, if the locale is set to en-AU it would be appropriate to look for translations in en-AU then en. Thats a simple example. A more complex example might be if the locale is set to :nd then the fallback chain (assuming a default locale of :en) would be:

iex> MyApp.Cldr.Locale.fallback_locale_names(:nb)
{:ok, [:nb, :no, :en]}

The "good news" is that this can be implemented as a non-breaking change to the current code. A secondly, since ex_cldr can derive the fallback chains from the CLDR data the integration is greatly simplified.

Idea: intermediary macro to remove boilerplate and get cleaner api

There is a lot of boilerplate and duplicated code needed with the current version of Trans. For example, you are defining the translated fields three times (use statement, fields(), Translation.Fields).

We are solving most of those things with an intermediary macro. I understand this is probably not one size fits it all, but maybe there is some inspiration for a future version and other users.

We ended up with something like this.

defmodule Marko.Schema do
  use Marko, :typed_schema

  @required_fields [:key]
  @optional_fields [:this]

  typed_schema "categories" do
    field(:key, :string)
    field(:this, :string)

    translate_field(:foo)
    translate_field(:bar)
    embed_translations()

    timestamps(type: :naive_datetime_usec)
  end

  @spec changeset(schema :: t, params :: Marko.params()) :: Changeset.t()
  def changeset(schema, params \\ %{}) do
    schema
    |> cast(params, @required_fields ++ @optional_fields)
    |> validate_required(@required_fields)
    |> cast_translations()
  end

And the magic happens here

defmodule Marko.Translation do
  @moduledoc """
  Translation are based on https://github.com/crbelaus/trans

  This module handles a lot of the needed boilerplate and allows for a cleaner api.

  To enable translation:

  - call `translate_field(:foo)` in the schema
  - call `embed_translations()` in the schema
  - call `cast_translations()` in the changeset
  - create a database field `add(:translations, :map, null: false)`
  """

  import Ecto.Changeset, only: [cast: 3, cast_embed: 2, cast_embed: 3]

  @spec trans :: Macro.t()
  def trans() do
    quote do
      # We use the module attribute `:translated_fields` as storage for our macro

      Module.register_attribute(__MODULE__, :translated_fields, persist: true, accumulate: true)

      import Marko.Translation, only: [translate_field: 1, cast_translations: 1, embed_translations: 0]

      @before_compile Marko.Translation
    end
  end

  defmacro __before_compile__(env) do
    case Module.get_attribute(env.module, :translated_fields) do
      [] ->
        nil

      fields ->
        quote do
          use Trans, translates: unquote(fields)

          defmodule Translations.Fields do
            use Marko, :typed_schema

            @required_fields unquote(fields)

            @primary_key false
            typed_embedded_schema do
              for f <- unquote(fields) do
                field(f, :string)
              end
            end

            @spec changeset(schema :: t, params :: Marko.params()) :: Ecto.Changeset.t(t)
            def changeset(schema, params \\ %{}) do
              schema
              |> cast(params, @required_fields)
              |> validate_required(@required_fields)
            end
          end
        end
    end
  end

  defmacro translate_field(field) do
    quote do
      @translated_fields unquote(field)

      field(unquote(field), :string)
    end
  end

  defmacro embed_translations() do
    quote do
      Ecto.Schema.embeds_one :translations, Translations, on_replace: :update, primary_key: false do
        for locale <- MarkoCldr.known_locale_names() do
          Ecto.Schema.embeds_one(locale, __MODULE__.Fields, on_replace: :update)
        end
      end
    end
  end

  @spec cast_translations(Ecto.Changeset.t()) :: Ecto.Changeset.t()
  def cast_translations(changeset) do
    cast_embed(changeset, :translations,
      required: true,
      with: fn schema, params ->
        changeset = cast(schema, params, [])

        Enum.reduce(MarkoCldr.known_locale_names(), changeset, &cast_embed(&2, &1))
      end
    )
  end
end

Set guidelines for contributions

Adding a CONTRIBUTING.md file would be a great way to guide contributors and explain the PR and release organization that I expect.

Installation part missing in documentation

Major issue πŸ˜„

Something like:

add {:trans, "~> 2.0"} to deps in mix.exs

is missing. Just noticed it because I wanted to copy/paste it and I'm very lazy 😏

Update dependency to Poison 3.1

Trans can't be installed using Hex without manual labour.

Failed to use "poison" because
ecto (version 2.1.4) requires ~> 2.2 or ~> 3.0
trans (version 2.0.0) requires ~> 2.1
mix.lock specifies 3.1.0

Could you check if the poison requirement can be updated?

Keep a changelog

Trans should keep a changelog stating its changes accross time. This site explains it perfectly:

What’s the point of a change log?
To make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project.

Why should I care?
Because software tools are for people. If you don’t care, why are you contributing to open source? Surely, there must be a kernel (ha!) of care somewhere in that lovely little brain of yours.

Performance and Convinience: Indexing translations fields separately or use of postgres operator @> and GIN index

First of all, Trans is a great tool which I'm looking forward to use extensively in my projects, thanks for sharing!
For now it's just a question but may be it will become an enhancement proposal.
Filtering big table by many translated fields in translations column using translated macros in where clauses will require indexing in order to be fast enough.
So, what is the best way to do it?
Should we add the btree indexes for every field inside every language in translations?
May be it is better to add GIN index to the whole translations column and use @> operator for filtering instead of x->y->>z operators?
Possibly add one more macro like existing translated macro or add an argument to translated macro for such possibility?
Also may be the documentation needs some more info about indexing for improving performance.

Test on more elixir versions

Currently Trans is being tested on Elixir 1.2.5 and 1.3.2, and OTP 18.3 and 19.0.

Elixir is now on the version 1.3.4 and OTP in 19.1. Those versions should be added to the test matrix.

Using changesets with trans

The docs do mention how to use a changeset with trans. My code is as follows:

defmodule Company.Product do
  use Ecto.Schema
  use Trans, translates: [:name, :description]
  import Ecto.Changeset

  alias __MODULE__

  defmodule Translation do
    use Ecto.Schema

    @primary_key false
    embedded_schema do
      field :name, :string
      field :description, :string
    end
  end

  schema "products" do
    field :name, :string
    field :description, :string
    field :price, :integer
    field :image_slug, :string

    embeds_one :translations, Translations, on_replace: :update, primary_key: false do
      embeds_one :sv, Product.Translation, on_replace: :update
      embeds_one :fi, Product.Translation, on_replace: :update
    end

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :description, :price, :image_slug])
    |> cast_embed(:translations) # is cast_embed the right option here?
    |> validate_required([:name, :description, :price, :image_slug])
  end
end

Using cast_embed returns the following error:

** (UndefinedFunctionError) function Company.Product.Translations.changeset/2 is undefined or private. Did you mean:
     
           * __changeset__/0

What is the correct way to use trans with a changeset?

Revamp tests

Currently all tests are done in a single test case. It would be better to separate tests concerning the QueryBuilder module and tests concerning the Translator module.
Also, the database is being manually populated with test data. This could be done much more easily using
Ex Machina.

Trans.QueryBuilder does not support join

Hi @crbelaus

I'm currently evaluating different solutions to tackle I18n in our project. trans looks very promising. Thank you for it!

Using Trans.QueryBuilder in conjunction with join does not seem to work.

from(o in Organisation, join: p in assoc(o, :programs), select: %{title: translated_as(Program, p.title, user.locale)}) |> Repo.all()

results in the following error:

** (Postgrex.Error) ERROR 42703 (undefined_column) column "p0" does not exist
     
query: SELECT translate_field(p0, $1::varchar, $2::varchar, $3::varchar, $4::varchar[]) AS "title" FROM "organisations" AS o0 INNER JOIN "programs" AS p1 ON p1."organisation_id" = o0."id"

Renaming the intermediate veriable p to p1 seems to resolve this issue.

    from(o in Organisation,
      join: p1 in assoc(o, :programs),
      select: %{title: translated_as(Program, p1.title, user.locale)}
    )
    |> Repo.all()

This in turn does not seem to be a maintainable solution. Any thoughts on this?

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.