Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: frint-intl #385

Open
2 tasks
fahad19 opened this issue Dec 11, 2017 · 5 comments
Open
2 tasks

Proposal: frint-intl #385

fahad19 opened this issue Dec 11, 2017 · 5 comments

Comments

@fahad19
Copy link
Member

fahad19 commented Dec 11, 2017

(Intended to be done outside of this repository)

Very much a work in progress, and needs to be refined further as more feedback keeps coming in.

Todo

  • Figure out culture
  • CLI usage

Background

To unify l10n/i18n usage in FrintJS Apps, a common API can be considered, instead of having different variations spread across multiple Child Apps.

Generic requirements

  • API to translate/localize labels (text blocks identified by keys) programmatically (just JS)
  • React components for embedding labels
  • Tool for finding unused/redundant labels in code base (that we can safely remove)

Proposed packages

The requirements can be handled by these proposed packages below.

The data structure for labels is expected to be like this:

import { createModel, createCollection, Types } from 'frint-data';

const Label = createModel({
  schema: {
    // unique ID
    name: Types.string, 

    // ICU Message Syntax
    value: Types.string, 
  },
});

const Labels = createCollection({
  model: Label,
});

Read more about ICU Message Syntax here: http://userguide.icu-project.org/formatparse/messages

frint-intl

Exports:

  • IntlService

Expected interface (nothing finalized of course):

class IntlService {
  constructor(options) {
    // options.data: Labels
  }

  format(key, ...formatWithData) {
    return String;
  }
}

frint-intl-react

React components for embedding at JSX level:

import React from 'react';
import { Format } from 'frint-intl-react';

function MyComponent(props) {
  const formatData = {
    brand: 'CTNL',
  };

  return (
    <div>
      <Format name="homepage.title" data={formatData} />
    </div>
  );
}

Or, format a custom ICU syntax message at component-level:

<Format message="ICU formatted text here..." />

frint-intl-cli

This will expose a $ frint intl subcommand in CLI, via which we can find out about unused labels in our code base:

$ frint intl find-unused-labels ./**/*.js --source allLabels.json

It will first load all the label names as available in allLabels.json file, and then execute a search in all JS files matching the pattern ./**/*.js. If there are any labels usage found that do no exist in allLabels.json, it will print line with file name and line number in console.

Usage

To set up your App:

import { createApp } from 'frint';
import { IntlService } from 'frint-intl';

const App = createApp({
  name: 'MyRootApp',
  providers: [
    {
      name: 'intl',
      useFactory() {
        return new IntlService({
          data: [
            { 
              name: 'homepage.title',
              value: 'Welcome to {{brand}}!',
            },
          ],
        });
      },
    },
  ],
});

In your component, now you can do:

import { Format } from 'frint-intl-react';

function () {
  return (
    <Format 
      name="homepage.title" 
      data={{brand: 'Foo'}} 
    />;
  );
}
@fahad19
Copy link
Member Author

fahad19 commented Jan 12, 2018

Repository created here: https://github.com/frintjs/frint-intl

@viacheslaff
Copy link
Contributor

Good idea! I like the usage of standard internationalization format and using DI to supply IntlService.

I have a concern though regarding whether implementation of the translations should be reactive or not. By reactive I mean the ability of <Format /> component to subscribe and react to changes in translations vs just rendering once and staying the same. Main concern is performance.

Reactive approach would be useful for:

  1. switching language without reloading the page (nice to have)
  2. rendering UI without translations while waiting for them i.e. with code-splitting (maybe fast, but is a poor user experience because of blinking)

Performance, especially at the time of initial rendering, is a concern because there will be hundreds of translations on a page -> hundreds of subscriptions to observables. I wonder if it'll degrade performance significantly. Maybe using React context will be more performant way to deliver IntlService. I'm thinking of doing a performance test.

@juliamaksimchik
Copy link

juliamaksimchik commented Jan 12, 2018

Well done, @fahad19!

Would be also nice to implement some kind of configuration object for whole IntlService. In our current solution, we have a functionality that should show label keys instead of value. That has been implemented to help content managers find an appropriate label key that used on a page.

@viacheslaff
Copy link
Contributor

We've discussed my comment with @fahad19 offline and we decided to go for synchronous implementation as a first step. Then we'll see whether there's need for reactive implementation.

@fahad19
Copy link
Member Author

fahad19 commented Jan 12, 2018

@juliamaksimchik: we can definitely look into this use case!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants