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

Feature: Locale Provider #434

Merged
merged 23 commits into from
Sep 1, 2021
Merged

Feature: Locale Provider #434

merged 23 commits into from
Sep 1, 2021

Conversation

kohashi
Copy link
Contributor

@kohashi kohashi commented Aug 16, 2021

Ref #312

Check List (If️ you added new component in this PR)

  • Export the component in src/components/index.ts
  • Add example to .storybook/documents/Information/Samples/Samples.stories.tsx

@netlify
Copy link

netlify bot commented Aug 16, 2021

✔️ Deploy Preview for ingred-ui ready!

🔨 Explore the source changes: 215ac11

🔍 Inspect the deploy log: https://app.netlify.com/sites/ingred-ui/deploys/612f1007e5e5dc00076088b8

😎 Browse the preview: https://deploy-preview-434--ingred-ui.netlify.app

@kohashi
Copy link
Contributor Author

kohashi commented Aug 16, 2021

TODO:

  • <LocaleProvider locale={jaJP}> とかで注入できるようにする
  • jaJP, zhCN, enUS まで定義
  • story上で言語選択可に

@kohashi kohashi marked this pull request as ready for review August 21, 2021 19:11
@kohashi kohashi requested a review from a team as a code owner August 21, 2021 19:11
@kohashi kohashi requested review from youchann and removed request for a team August 21, 2021 19:11
@kohashi
Copy link
Contributor Author

kohashi commented Aug 21, 2021

レビュー可能になった気がするのでどなたかお願いします。

@youchann
Copy link
Contributor

今日のお昼あけあたりにみます!

Copy link
Contributor

@youchann youchann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

とりあえずこんなところでしょうか。
基本相談っぽい感じになりましたが、説明が足りない場合はどんどん聞いちゃってください!

@@ -0,0 +1,3 @@
import styled from "styled-components";

export const Container = styled.div``;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yarn generateしていただいたと思うのですが、不要なら消しちゃう方針で大丈夫です:+1:

defaultProps: { activeText: "开", inActiveText: "关" },
},
ConfirmModal: {
defaultProps: { confirmText: "确认", cancelText: "取消" },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

取消だけ読めた..w

const defaultProps = locale?.components?.[name].defaultProps;
let propName;

for (propName in defaultProps) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分はいつも下記のような書き方をするのですが、letを用いる/用いないでなにか差が出たりしますか..?(純粋に気になっただけです)

Suggested change
for (propName in defaultProps) {
for (const propName in defaultProps) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hoistingも一度しか起きないしブロックスコープでconstを再定義するよりも早くなる余地があるからです。(実務上の件数ではおそらく計測不能なほどの高速化ですが)
…と思ったけど試したことがないので

// 準備
const PROPS_COUNT = 100000;
const props = {};
for (let i = 0; i < PROPS_COUNT; i++) {
  props[i.toString()] = i.toString();
}

// constのやつ
console.time();
for (const outKey in props) {
  // doSomething
}
console.timeEnd(); // default: 9.113037109375 ms

// letのやつ
console.time();
let outKey
for (outKey in props) {
  // doSomething
}
console.timeEnd(); // default: 6.7509765625 ms

どうでしょう!
初回のテストではやはり letのほうが高速 でした!

・・・というのが初回のテストだったんですが、初回がチャンピオンデータだったようで
再読込するたびにほぼ同じになったり、constのほうが高速になったり、逆だったり、ばらつきが激しいです。

デバッグコンソールの実行では? 読み込み時では? といろいろ試したもののばらつきは変わらず。
https://codepen.io/kohashi/pen/GRERMVw とかで書いてみたけど

ちなみに node ( v15.13.0 ) でもためしてみました。こちらは安定していて

default: 14.598ms // constのほう
default: 7.452ms // letのほう

let のほうが早い! しかもダブルスコアで!!!

しかし、ふと古いnodeだとどうだろう?と思い v8.17.0 (特に理由はなくなんとなく v8 latest)で試すと

undefined: 8.979ms // constのほう
undefined: 8.476ms // letのほう

・・・ 🤔

まぁ console.time の仕様が違うくらい昔なので何らかの実装が違うようでした。

というわけで「実務上差がないレベルで let 使ったほうが早いと思ったんだけど、計測してみたらなんかブロックスコープの生成か何かが実装によってバラバラっぽいため速度もバラバラ。node v15では安定して早いっぽいが」

そんな感じでした。

Comment on lines 23 to 25
if (output[propName] === undefined) {
output[propName] = defaultProps[propName];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type HogeProps = {
  hoge: {
    text: "hogehoge";
    someValue: true;
  }
};

こんな感じのネストした値には対応できていない気がするのですがどうでしょう...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/TehShrike/deepmerge

このnpmで説明できるかは微妙ですが、オブジェクト同士をマージするような形式(undefinedの場合のみ上書き)みたいなやり方が良いかなぁ今の所思っております!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propsのマージ、 shallow merge, or deep merge の話ですよね。
ここはshallowでいいんじゃないかって思ってました。

const ToggleButton: React.FunctionComponent<ToggleButtonProps> = ({
   activeText = "ON", // propsを受けてデフォ値を代入する、というのと、l10nはよく似ている
   hoge = { text: "hogehoge", someValue: true} // こういうのと感覚的には同じだから、shallowがよりよいんじゃないかなって
}) => {
   // ....

速度的な観点もありはするものの、基本的にはl10nする側の定義は大きくないので、deepmergeが必要ならやってもよいとは思いますー。
(MUIはシャローマージにしているものの、だから使い勝手悪くてあまり定義がないのか、そもそも基本UIフレームワークにl10nが必要な文字ってそんなに無いのでは?ということなのかの意図は判然としない…後者の気がしますが)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そもそも基本UIフレームワークにl10nが必要な文字ってそんなに無いのでは?

なるほど、MUIもshallowなのですね。
deepmergeへ途中から変更するのもそこまで難しくないので、現段階ではshallowでいく、というので良い気がしました!

ここ書いてて思ったのですが、<MultipleFilter />も対応が必要そうですね。

* @param params
* @returns localized Props.
*/
export function useLocaleProps<T>(params: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この"カスタムフック"に該当するものはhooks/に集めているのでそちらに移動しても良さそうです:+1:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hoooksのほうがベター。なるほどです。移しますー

};
}

export const jaJP: Localization = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このあたりの変数、ライブラリの利用者からも使えるようにする方が良い気がしておりまして。

src/index.tsxあたりからexportすると良さそうです!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exportしましたー

};

return (
<LocaleProvider locale={locales[localeOption.value]}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【相談】
storybookで書けているので良いかなぁと思いつつも、READMEの方も<LocaleProvider />を用いるようにしたら親切かなと思うのですがどうですかね...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どうでしょうね。。
READMEには最小限だけ書いて、別途docsをしっかり書くのが流行りというか主流ですが、今はstorybookがDocs代わりになってるならいらないのでは?とも思ったりします。

「あんまりl10n周りを READMEの最初にガッツリ書かないよなー」くらいの温度感です。

@kohashi kohashi changed the title wip: impl locale provider Feature: Locale Provider Aug 23, 2021
@kohashi
Copy link
Contributor Author

kohashi commented Aug 24, 2021

一旦 残りを整理

  • <MultipleFilter /> もl10n対応
    • エラーメッセージとからしい
  • localesをexportして利用者がカスタムできる
    • 利用者が新たなlocale (e.g. koKR とか) を定義できる
    • 利用者が既存locale定義の一部修正ができる (e.g. jaJP でカタカナ オン・オフ を英語に戻すなど)

@kohashi
Copy link
Contributor Author

kohashi commented Aug 27, 2021

|| " で検索すると、 <MultipleFilter /> 以外にもいくつかありそう。
props のデフォ値としてではなく、tsxのインラインで <Hoge hogeText={propsValue || "default message"} みたいな記述。

EditFilterCard, FilterCard, かな

@kohashi
Copy link
Contributor Author

kohashi commented Aug 27, 2021

EditFilterCard

  • formPlaceholder
  • inputErrorText
  • formErrorText
  • editButtonTitle
  • sectionTitle, conditionTitle

FilterCard

  • formPlaceholder
  • inputErrorText
  • formErrorText
  • applyButtonTitle
  • sectionTitle, conditionTitle

違いは editButtonTitleかapplyButtonTitleかの違いかな。
それと MultipleFilter の placeholder

@kohashi
Copy link
Contributor Author

kohashi commented Aug 30, 2021

すごい大変だった…。

@youchann お元気になったらレビューお願いします。議論になりそうなとこはコードにコメントしておきます。

title: "把文件拖入, 同样支持点击上传。",
},
},
ItemEmpty: { defaultProps: { title: "未找到。" } },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

翻訳する量が増えちゃったのと、中国語チェックしてくれるヒトが忙しくなっちゃったので、中国語版は半分くらいしかない…。

);
const ItemEmpty: React.FunctionComponent<ItemEmptyProps> = (inProps) => {
const props = useLocaleProps({ props: inProps, name: "ItemEmpty" });
const { title = "Not found.", subtitle, emptyImage, imageWidth = 80 } = props;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ItemEmpty の NotFound も useLocaleProps の対象にしました。

@@ -212,7 +218,7 @@ const MultipleFilter: React.FunctionComponent<MultipleFilterProps> = ({
ref={setInputElement}
readOnly
type="text"
placeholder={placeholder ?? "Add a new filter"}
placeholder={placeholder}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こういう ?? Nullish Coalescing だったところを、 props = 'Default Value' に移す & useLocaleProps で上書きみたいなことを何箇所かでやっています。

?? "|| " で検索したのでl10n必要なとこは一旦埋めた気がするけど、まだ大きくあれば一旦マージして別PRにて対応したく…

@@ -187,7 +195,7 @@ export const FilterCard: React.FunctionComponent<Props> = ({
{selectedFilter && (
<div>
<Typography weight="bold" size="lg">
{filter?.conditionTitle || "Condition"}
{filter?.conditionTitle || conditionTitle}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここが一番迷ったんですが、 conditionTitle, sectionTitle は props に移設しました。

props に移設しないやり方としては、

    FilterCard: {
      defaultProps: {
        applyButtonTitle: "適用",
        inputErrorText: "入力してください",
        formPlaceholder: "検索",
        sectionTitle: "セクション",
        conditionTitle: "条件",
      },
    },
// ↓
    FilterCard: {
      defaultProps: {
        applyButtonTitle: "適用",
        inputErrorText: "入力してください",
        formPlaceholder: "検索",
      },
      texts: {
        sectionTitle: "セクション",
        conditionTitle: "条件",
      },
    },

こんな風にすることも考えたんですが、一旦propsに統一したほうが利用者側でカスタマイズが容易で良いかな、と。

Copy link
Contributor

@youchann youchann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTMです!

@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LocaleProvider component testing LocaleProvider 1`] = `<DocumentFragment />`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

思えばProviderって実態の無いUIなのでスナップショットも不要な気がしますね!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにそうだなって感じたので、スナップショット除外して生成出来るかだけのテスト(toBeTruthy)にしました!

@kohashi
Copy link
Contributor Author

kohashi commented Sep 1, 2021

セルフマージしちゃっていいのかな

@kohashi
Copy link
Contributor Author

kohashi commented Sep 1, 2021

reviewed になったら権限あるひとはセルフマージしてるな。よーししちゃうぞー

@kohashi kohashi merged commit d403ac9 into master Sep 1, 2021
@kohashi kohashi deleted the impl/locale branch September 1, 2021 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants