@smarthr/i18n 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,85 @@
1
1
  # @smarthr/i18n
2
2
 
3
3
  SmartHRの国際化(i18n)ユーティリティライブラリです。
4
+
5
+ ## 主な機能
6
+
7
+ - ロケールの取得
8
+ - Reactコンポーネント向けの国際化サポート
9
+
10
+ ## サブパッケージ
11
+
12
+ - [`get-locale`](./src/get-locale/README.md): 複数の情報源から最適な言語コードを判定して返します。
13
+ - [`use-intl`](./src/use-intl/README.md): `use-intl`または`next-intl`ライブラリを利用して国際化(i18n)機能を提供するカスタムReactフックです。
14
+
15
+ ## インストール
16
+
17
+ ```bash
18
+ npm install @smarthr/i18n
19
+ ```
20
+
21
+ ## 利用方法
22
+
23
+ 各サブパッケージの詳細な使い方は、上記リンク先のREADMEをご参照ください。
24
+
25
+ ### Next.jsでの使用例
26
+
27
+ [next-intlのGetting started](https://next-intl.dev/docs/getting-started)に従って`next-intl`の環境をセットアップします。
28
+ ここではApp Routerでの[i18nルーティングを使わないパターン](https://next-intl.dev/docs/getting-started/app-router/without-i18n-routing)で`@smarthr/i18n`を利用する例を記載します。
29
+
30
+ `request.ts`ファイルで`getLocale`を利用してアプリケーションがサポートする言語からロケールを決定します。`currentLocale`の取得はアプリケーションごとに実装するものとします。
31
+
32
+ ```ts
33
+ import { getRequestConfig } from 'next-intl/server'
34
+ import { getLocale, Locale } from '@smarthr/i18n'
35
+ import { getUserLocale } from '@/getUserLocale'
36
+
37
+ const supportedLocales: Locale[] = ['ja-JP', 'en-US']
38
+
39
+ export default getRequestConfig(async () => {
40
+ const currentLocale = await getUserLocale()
41
+
42
+ const locale = getLocale({
43
+ currentLocale,
44
+ supportedLocales,
45
+ })
46
+ return {
47
+ locale,
48
+ messages: (await import(`./locales/${locale}.json`)).default,
49
+ }
50
+ })
51
+ ```
52
+
53
+ `layout.tsx`で`NextIntlClientProvider`を適用します。
54
+
55
+ ```tsx
56
+ import { NextIntlClientProvider } from 'next-intl'
57
+ import { getLocale } from 'next-intl/server'
58
+
59
+ export default async function RootLayout({
60
+ children,
61
+ }: Readonly<{
62
+ children: React.ReactNode
63
+ }>) {
64
+ const locale = await getLocale()
65
+ return (
66
+ <html lang={locale}>
67
+ <body>
68
+ <NextIntlClientProvider>{children}</NextIntlClientProvider>
69
+ </body>
70
+ </html>
71
+ )
72
+ }
73
+ ```
74
+
75
+ 多言語化を適用したいページで`useNextIntl`を使います。
76
+ React Server Componentsで使う場合は[ワークアラウンド](./src/use-intl/README.md#react-server-componentsで使う場合のワークアラウンド)を利用します。
77
+
78
+ ```tsx
79
+ import { useNextIntl } from '@smarthr/i18n'
80
+
81
+ export default function Home() {
82
+ const { formatMessage } = useNextIntl()
83
+ return <div>{formatMessage('Home.greeting')}</div>
84
+ }
85
+ ```
@@ -0,0 +1,38 @@
1
+ /**
2
+ * サポートされているロケール一覧:
3
+ *
4
+ * ja-JP: 日本語
5
+ * en-US: 英語
6
+ * ko-KR: 韓国語
7
+ * zh-Hant-TW: 繁体中文
8
+ * zh-Hans-CN: 簡体中文
9
+ * vi-VN: ベトナム語
10
+ * pt-BR: ポルトガル語
11
+ * ja-JP-x-easy: やさしい日本語
12
+ * id-ID: インドネシア語
13
+ */
14
+ declare const SUPPORTED_LOCALES: readonly ["ja-JP", "en-US", "ko-KR", "zh-Hant-TW", "zh-Hans-CN", "vi-VN", "pt-BR", "ja-JP-x-easy", "id-ID"];
15
+ export type Locale = (typeof SUPPORTED_LOCALES)[number];
16
+ /**
17
+ * 対応する言語コードを判定して返します。
18
+ *
19
+ * 判定優先順位:
20
+ * 1. shouldReturnDefaultLanguage が true の場合はデフォルト言語
21
+ * 2. currentLocale が supportedLocales に含まれていればそれを返す
22
+ * 3. cookieKey で指定したcookieの値が supportedLocales に含まれていればそれを返す
23
+ * 4. ブラウザの言語設定(navigator.languages)に対応するものがあればそれを返す
24
+ * 5. どれにも該当しなければデフォルト言語
25
+ *
26
+ * @param params.currentLocale - 現在の言語コード(DBの値や固定値など、nullの場合はスキップ)
27
+ * @param params.supportedLocales - アプリが対応する言語コード配列
28
+ * @param params.shouldReturnDefaultLanguage - 常にデフォルト言語を返す場合にtrue
29
+ * @param params.cookieKey - 取得するcookieのキー(省略時は'selectedLocale')
30
+ * @returns 判定された言語コード
31
+ */
32
+ export declare function getLocale(params: {
33
+ currentLocale: Locale | null;
34
+ supportedLocales: Locale[];
35
+ shouldReturnDefaultLanguage?: boolean;
36
+ cookieKey?: string;
37
+ }): Locale;
38
+ export {};
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getLocale = getLocale;
4
+ /**
5
+ * サポートされているロケール一覧:
6
+ *
7
+ * ja-JP: 日本語
8
+ * en-US: 英語
9
+ * ko-KR: 韓国語
10
+ * zh-Hant-TW: 繁体中文
11
+ * zh-Hans-CN: 簡体中文
12
+ * vi-VN: ベトナム語
13
+ * pt-BR: ポルトガル語
14
+ * ja-JP-x-easy: やさしい日本語
15
+ * id-ID: インドネシア語
16
+ */
17
+ var SUPPORTED_LOCALES = [
18
+ 'ja-JP',
19
+ 'en-US',
20
+ 'ko-KR',
21
+ 'zh-Hant-TW',
22
+ 'zh-Hans-CN',
23
+ 'vi-VN',
24
+ 'pt-BR',
25
+ 'ja-JP-x-easy',
26
+ 'id-ID',
27
+ ];
28
+ var DEFAULT_LANGUAGE = 'ja-JP';
29
+ /**
30
+ * 対応する言語コードを判定して返します。
31
+ *
32
+ * 判定優先順位:
33
+ * 1. shouldReturnDefaultLanguage が true の場合はデフォルト言語
34
+ * 2. currentLocale が supportedLocales に含まれていればそれを返す
35
+ * 3. cookieKey で指定したcookieの値が supportedLocales に含まれていればそれを返す
36
+ * 4. ブラウザの言語設定(navigator.languages)に対応するものがあればそれを返す
37
+ * 5. どれにも該当しなければデフォルト言語
38
+ *
39
+ * @param params.currentLocale - 現在の言語コード(DBの値や固定値など、nullの場合はスキップ)
40
+ * @param params.supportedLocales - アプリが対応する言語コード配列
41
+ * @param params.shouldReturnDefaultLanguage - 常にデフォルト言語を返す場合にtrue
42
+ * @param params.cookieKey - 取得するcookieのキー(省略時は'selectedLocale')
43
+ * @returns 判定された言語コード
44
+ */
45
+ function getLocale(params) {
46
+ var currentLocale = params.currentLocale, supportedLocales = params.supportedLocales, _a = params.shouldReturnDefaultLanguage, shouldReturnDefaultLanguage = _a === void 0 ? false : _a, _b = params.cookieKey, cookieKey = _b === void 0 ? 'selectedLocale' : _b;
47
+ // デフォルト言語を返すべき場合
48
+ if (shouldReturnDefaultLanguage)
49
+ return DEFAULT_LANGUAGE;
50
+ if (currentLocale === null)
51
+ return DEFAULT_LANGUAGE;
52
+ if (supportedLocales.includes(currentLocale))
53
+ return currentLocale;
54
+ // サーバーサイドのエラー回避
55
+ if (typeof window === 'undefined')
56
+ return DEFAULT_LANGUAGE;
57
+ // cookieから言語コードを取得
58
+ var cookieLocale = getCookieLocale(cookieKey);
59
+ // cookieに言語コードが存在する場合、対応する言語コードを返す
60
+ if (cookieLocale && supportedLocales.includes(cookieLocale))
61
+ return cookieLocale;
62
+ // ブラウザの言語設定を取得し、最も優先度が高い有効な言語を返却する
63
+ var browserLanguages = getBrowserLanguages().map(function (lang) { return lang.toLowerCase(); });
64
+ for (var _i = 0, browserLanguages_1 = browserLanguages; _i < browserLanguages_1.length; _i++) {
65
+ var lang = browserLanguages_1[_i];
66
+ var convertedLang = convertLang(lang);
67
+ if (supportedLocales.includes(convertedLang)) {
68
+ return convertedLang;
69
+ }
70
+ }
71
+ return DEFAULT_LANGUAGE;
72
+ }
73
+ // ブラウザの言語設定を取得
74
+ // navigator.languages: ユーザーがブラウザに設定している優先言語リスト。最初の要素が最も優先度が高い
75
+ // 例: ['ja-JP', 'en-US', 'en'] -> 日本語を優先し、次に英語(米国)、最後に英語全般
76
+ // navigator.languages が存在しないブラウザの場合は navigator.language(単一の設定)を使用
77
+ var getBrowserLanguages = function () {
78
+ if (navigator.languages && navigator.languages.length > 0) {
79
+ return navigator.languages;
80
+ }
81
+ return navigator.language ? [navigator.language] : [];
82
+ };
83
+ var convertLang = function (lang) {
84
+ // jaから始まる場合はjaに変換
85
+ if (lang.startsWith('ja'))
86
+ return 'ja-JP';
87
+ // idから始まる場合はidに変換
88
+ if (lang.startsWith('id'))
89
+ return 'id-ID';
90
+ // enから始まる場合はen-usに変換
91
+ if (lang.startsWith('en'))
92
+ return 'en-US';
93
+ // ptから始まる場合はptに変換
94
+ if (lang.startsWith('pt'))
95
+ return 'pt-BR';
96
+ // viから始まる場合はviに変換
97
+ if (lang.startsWith('vi'))
98
+ return 'vi-VN';
99
+ // koから始まる場合はkoに変換
100
+ if (lang.startsWith('ko'))
101
+ return 'ko-KR';
102
+ // zh-cn, zh-hansから始まる場合はzh-cnに変換
103
+ if (lang.startsWith('zh-cn') || lang.startsWith('zh-hans'))
104
+ return 'zh-Hans-CN';
105
+ // zh-tw, zh-hant, zh-hkから始まる場合はzh-twに変換
106
+ if (lang.startsWith('zh-tw') || lang.startsWith('zh-hant') || lang.startsWith('zh-hk'))
107
+ return 'zh-Hant-TW';
108
+ // 上記のチェックに当てはまらなかったzhから始まるものはzh-cnに変換
109
+ if (lang.startsWith('zh'))
110
+ return 'zh-Hans-CN';
111
+ return DEFAULT_LANGUAGE;
112
+ };
113
+ /**
114
+ * cookieから指定されたキーの値を取得し、有効なロケールの場合のみ返す
115
+ */
116
+ var getCookieLocale = function (cookieKey) {
117
+ var _a;
118
+ // サーバーサイドではcookieが利用できないためnullを返す
119
+ if (typeof document === 'undefined')
120
+ return null;
121
+ // cookieの値を取得
122
+ var cookieValue = (_a = document.cookie
123
+ .split('; ')
124
+ .find(function (row) { return row.startsWith("".concat(cookieKey, "=")); })) === null || _a === void 0 ? void 0 : _a.split('=')[1];
125
+ // cookieの値が存在しない場合はnullを返す
126
+ if (!cookieValue)
127
+ return null;
128
+ // 有効なロケールの場合のみ返す
129
+ return validateLocale(cookieValue) ? cookieValue : null;
130
+ };
131
+ var validateLocale = function (locale) { return SUPPORTED_LOCALES.includes(locale); };
132
+ //# sourceMappingURL=getLocale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getLocale.js","sourceRoot":"","sources":["../../src/get-locale/getLocale.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { type Locale, getLocale } from './get-locale/getLocale';
2
+ export { useIntl } from './use-intl/useIntl';
3
+ export { useNextIntl } from './use-intl/useNextIntl';
4
+ export { useIntlImpl } from './use-intl/useIntlImpl';
package/lib/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useIntlImpl = exports.useNextIntl = exports.useIntl = exports.getLocale = void 0;
4
+ var getLocale_1 = require("./get-locale/getLocale");
5
+ Object.defineProperty(exports, "getLocale", { enumerable: true, get: function () { return getLocale_1.getLocale; } });
6
+ var useIntl_1 = require("./use-intl/useIntl");
7
+ Object.defineProperty(exports, "useIntl", { enumerable: true, get: function () { return useIntl_1.useIntl; } });
8
+ var useNextIntl_1 = require("./use-intl/useNextIntl");
9
+ Object.defineProperty(exports, "useNextIntl", { enumerable: true, get: function () { return useNextIntl_1.useNextIntl; } });
10
+ var useIntlImpl_1 = require("./use-intl/useIntlImpl");
11
+ Object.defineProperty(exports, "useIntlImpl", { enumerable: true, get: function () { return useIntlImpl_1.useIntlImpl; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oDAA+D;AAAzC,sGAAA,SAAS,OAAA;AAC/B,8CAA4C;AAAnC,kGAAA,OAAO,OAAA;AAChB,sDAAoD;AAA3C,0GAAA,WAAW,OAAA;AACpB,sDAAoD;AAA3C,0GAAA,WAAW,OAAA"}
@@ -0,0 +1,19 @@
1
+ import { type Messages } from 'use-intl';
2
+ /**
3
+ * use-intlを利用して国際化(i18n)ユーティリティを提供するカスタムReactフックです。
4
+ *
5
+ * @template OwnMessages - 翻訳に使用するメッセージの型。デフォルトは `Messages` です。
6
+ * @returns メッセージを整形する`formatMessage`関数を含むオブジェクトを返します。
7
+ *
8
+ * @example
9
+ * const intl = useIntl<MyMessages>();
10
+ * const greeting = intl.formatMessage({ id: 'greeting' });
11
+ */
12
+ export declare const useIntl: <OwnMessages extends Messages = Messages>() => {
13
+ formatMessage: <TargetKey extends { [Key in keyof OwnMessages & string]: OwnMessages[Key] extends string ? Key : `${Key}.${{ [Key_1 in keyof OwnMessages[Key] & string]: OwnMessages[Key][Key_1] extends string ? Key_1 : `${Key_1}.${{ [Key_2 in keyof OwnMessages[Key][Key_1] & string]: OwnMessages[Key][Key_1][Key_2] extends string ? Key_2 : `${Key_2}.${{ [Key_3 in keyof OwnMessages[Key][Key_1][Key_2] & string]: OwnMessages[Key][Key_1][Key_2][Key_3] extends string ? Key_3 : `${Key_3}.${{ [Key_4 in keyof OwnMessages[Key][Key_1][Key_2][Key_3] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4] extends string ? Key_4 : `${Key_4}.${{ [Key_5 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5] extends string ? Key_5 : `${Key_5}.${{ [Key_6 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6] extends string ? Key_6 : `${Key_6}.${{ [Key_7 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7] extends string ? Key_7 : `${Key_7}.${{ [Key_8 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8] extends string ? Key_8 : `${Key_8}.${{ [Key_9 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9] extends string ? Key_9 : `${Key_9}.${{ [Key_10 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9][Key_10] extends string ? Key_10 : `${Key_10}.${/*elided*/ any[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9][Key_10] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2] & string]}`; }[keyof OwnMessages[Key][Key_1] & string]}`; }[keyof OwnMessages[Key] & string]}`; }[keyof OwnMessages & string] = never, Strict extends boolean | undefined = true>(id: [Strict] extends [true] ? TargetKey : string, options?: {
14
+ strict?: Strict | undefined;
15
+ values?: {
16
+ [x: string]: string | number | Date | ((chunks: import("react").ReactNode) => import("react").ReactNode);
17
+ };
18
+ } | undefined) => any;
19
+ };
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useIntl = void 0;
5
+ var use_intl_1 = require("use-intl");
6
+ var useIntlImpl_1 = require("./useIntlImpl");
7
+ /**
8
+ * use-intlを利用して国際化(i18n)ユーティリティを提供するカスタムReactフックです。
9
+ *
10
+ * @template OwnMessages - 翻訳に使用するメッセージの型。デフォルトは `Messages` です。
11
+ * @returns メッセージを整形する`formatMessage`関数を含むオブジェクトを返します。
12
+ *
13
+ * @example
14
+ * const intl = useIntl<MyMessages>();
15
+ * const greeting = intl.formatMessage({ id: 'greeting' });
16
+ */
17
+ var useIntl = function () { return (0, useIntlImpl_1.useIntlImpl)(use_intl_1.useTranslations); };
18
+ exports.useIntl = useIntl;
19
+ //# sourceMappingURL=useIntl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIntl.js","sourceRoot":"","sources":["../../src/use-intl/useIntl.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;AACZ,qCAAyD;AAEzD,6CAA2C;AAE3C;;;;;;;;;GASG;AACI,IAAM,OAAO,GAAG,cAA+C,OAAA,IAAA,yBAAW,EAAc,0BAAe,CAAC,EAAzC,CAAyC,CAAA;AAAlG,QAAA,OAAO,WAA2F"}
@@ -0,0 +1,50 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * next-intl / use-intl のuseTranslationsから利用する rich APIのvaluesの型定義です。
4
+ */
5
+ type Values = Record<string, string | number | Date>;
6
+ type TagFunction = (chunks: ReactNode) => ReactNode;
7
+ type OptionValues = Record<string, Values[string] | TagFunction>;
8
+ /**
9
+ * 与えられた辞書型(Dictionary)の全てのキーを「ドット区切りの文字列」として再帰的に抽出する型です。
10
+ *
11
+ * 例えば、以下のような辞書型があるとします:
12
+ *
13
+ * type Messages = {
14
+ * hello: string;
15
+ * user: {
16
+ * name: string;
17
+ * profile: {
18
+ * age: string;
19
+ * };
20
+ * };
21
+ * }
22
+ *
23
+ * この場合、TargetKeys<Messages>は次のようなユニオン型になります:
24
+ *
25
+ * "hello" | "user.name" | "user.profile.age"
26
+ *
27
+ * - 文字列型(string)の値を持つキーはそのまま抽出されます。
28
+ * - オブジェクト型の場合は、キーをドットで連結して再帰的に展開します。
29
+ */
30
+ type TargetKeys<Dictionary> = {
31
+ [Key in keyof Dictionary & string]: Dictionary[Key] extends string ? Key : `${Key}.${TargetKeys<Dictionary[Key]>}`;
32
+ }[keyof Dictionary & string];
33
+ /**
34
+ * 国際化(i18n)のメッセージフォーマッターを提供するカスタムフックです。
35
+ *
36
+ * @template Messages - 翻訳メッセージの型を表します。
37
+ * @param useTranslations - 翻訳関数`t`を返すuse-intlまたはnext-intlのフックです。
38
+ * @returns メッセージを整形する`formatMessage`関数を含むオブジェクトを返します。
39
+ *
40
+ * @example
41
+ * const { formatMessage } = useIntlImpl<MyMessages>(useTranslations);
42
+ * const message = formatMessage('greeting', { values: { name: 'Alice' } });
43
+ */
44
+ export declare const useIntlImpl: <Messages>(useTranslations: any) => {
45
+ formatMessage: <TargetKey extends TargetKeys<Messages> = never, Strict extends boolean | undefined = true>(id: [Strict] extends [true] ? TargetKey : string, options?: {
46
+ strict?: Strict;
47
+ values?: OptionValues;
48
+ }) => any;
49
+ };
50
+ export {};
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.useIntlImpl = void 0;
15
+ var jsx_runtime_1 = require("react/jsx-runtime");
16
+ var react_1 = require("react");
17
+ /**
18
+ * 国際化(i18n)のメッセージフォーマッターを提供するカスタムフックです。
19
+ *
20
+ * @template Messages - 翻訳メッセージの型を表します。
21
+ * @param useTranslations - 翻訳関数`t`を返すuse-intlまたはnext-intlのフックです。
22
+ * @returns メッセージを整形する`formatMessage`関数を含むオブジェクトを返します。
23
+ *
24
+ * @example
25
+ * const { formatMessage } = useIntlImpl<MyMessages>(useTranslations);
26
+ * const message = formatMessage('greeting', { values: { name: 'Alice' } });
27
+ */
28
+ var useIntlImpl = function (useTranslations) {
29
+ var t = useTranslations();
30
+ /**
31
+ * 指定されたメッセージIDと任意の値を使って、メッセージを整形します。
32
+ *
33
+ * @param id - 整形するメッセージのキー。`strict`が`true`の場合はメッセージキーの厳密な型、そうでない場合は任意の文字列が許可されます。
34
+ * @param options - オプションの設定オブジェクト。
35
+ * @param options.strict - `true`の場合、メッセージキーの厳密な型チェックを行います。
36
+ * @param options.values - メッセージ内で補間する値。use-intl / next-intlのrichメソッドのフォーマットに対応しています。
37
+ * @returns 整形されたメッセージ。
38
+ */
39
+ var formatMessage = (0, react_1.useCallback)(function (id, options) {
40
+ return t.rich(id, __assign(__assign({}, options === null || options === void 0 ? void 0 : options.values), { br: function () { return (0, jsx_runtime_1.jsx)("br", {}); } }));
41
+ }, [t]);
42
+ return {
43
+ formatMessage: formatMessage,
44
+ };
45
+ };
46
+ exports.useIntlImpl = useIntlImpl;
47
+ //# sourceMappingURL=useIntlImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIntlImpl.js","sourceRoot":"","sources":["../../src/use-intl/useIntlImpl.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,+BAAmD;AAmCnD;;;;;;;;;;GAUG;AACI,IAAM,WAAW,GAAG,UAAY,eAAoB;IACzD,IAAM,CAAC,GAAG,eAAe,EAAE,CAAA;IAE3B;;;;;;;;OAQG;IACH,IAAM,aAAa,GAAG,IAAA,mBAAW,EAC/B,UACE,EAAgD,EAChD,OAGC;QAED,OAAA,CAAC,CAAC,IAAI,CAAC,EAAE,wBACJ,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAClB,EAAE,EAAE,cAAM,OAAA,gCAAM,EAAN,CAAM,IAChB;IAHF,CAGE,EACJ,CAAC,CAAC,CAAC,CACJ,CAAA;IAED,OAAO;QACL,aAAa,eAAA;KACd,CAAA;AACH,CAAC,CAAA;AA9BY,QAAA,WAAW,eA8BvB"}
@@ -0,0 +1,22 @@
1
+ import { type Messages } from 'next-intl';
2
+ /**
3
+ * next-intlを利用して国際化(i18n)ユーティリティを提供するカスタムReactフックです。
4
+ *
5
+ * このフックはClient Componentsでのみ動作します。
6
+ * React Server ComponentsはアプリケーションコードでuseIntlImplを利用して専用のフックを実装してください。
7
+ *
8
+ * @template OwnMessages - 翻訳に使用するメッセージの型。デフォルトは `Messages` です。
9
+ * @returns メッセージを整形する`formatMessage`関数を含むオブジェクトを返します。
10
+ *
11
+ * @example
12
+ * const intl = useNextIntl<MyMessages>();
13
+ * const greeting = intl.formatMessage({ id: 'greeting' });
14
+ */
15
+ export declare const useNextIntl: <OwnMessages extends Messages = Messages>() => {
16
+ formatMessage: <TargetKey extends { [Key in keyof OwnMessages & string]: OwnMessages[Key] extends string ? Key : `${Key}.${{ [Key_1 in keyof OwnMessages[Key] & string]: OwnMessages[Key][Key_1] extends string ? Key_1 : `${Key_1}.${{ [Key_2 in keyof OwnMessages[Key][Key_1] & string]: OwnMessages[Key][Key_1][Key_2] extends string ? Key_2 : `${Key_2}.${{ [Key_3 in keyof OwnMessages[Key][Key_1][Key_2] & string]: OwnMessages[Key][Key_1][Key_2][Key_3] extends string ? Key_3 : `${Key_3}.${{ [Key_4 in keyof OwnMessages[Key][Key_1][Key_2][Key_3] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4] extends string ? Key_4 : `${Key_4}.${{ [Key_5 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5] extends string ? Key_5 : `${Key_5}.${{ [Key_6 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6] extends string ? Key_6 : `${Key_6}.${{ [Key_7 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7] extends string ? Key_7 : `${Key_7}.${{ [Key_8 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8] extends string ? Key_8 : `${Key_8}.${{ [Key_9 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9] extends string ? Key_9 : `${Key_9}.${{ [Key_10 in keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9] & string]: OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9][Key_10] extends string ? Key_10 : `${Key_10}.${/*elided*/ any[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9][Key_10] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8][Key_9] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7][Key_8] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6][Key_7] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5][Key_6] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4][Key_5] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3][Key_4] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2][Key_3] & string]}`; }[keyof OwnMessages[Key][Key_1][Key_2] & string]}`; }[keyof OwnMessages[Key][Key_1] & string]}`; }[keyof OwnMessages[Key] & string]}`; }[keyof OwnMessages & string] = never, Strict extends boolean | undefined = true>(id: [Strict] extends [true] ? TargetKey : string, options?: {
17
+ strict?: Strict | undefined;
18
+ values?: {
19
+ [x: string]: string | number | Date | ((chunks: import("react").ReactNode) => import("react").ReactNode);
20
+ };
21
+ } | undefined) => any;
22
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useNextIntl = void 0;
4
+ var next_intl_1 = require("next-intl");
5
+ var useIntlImpl_1 = require("./useIntlImpl");
6
+ /**
7
+ * next-intlを利用して国際化(i18n)ユーティリティを提供するカスタムReactフックです。
8
+ *
9
+ * このフックはClient Componentsでのみ動作します。
10
+ * React Server ComponentsはアプリケーションコードでuseIntlImplを利用して専用のフックを実装してください。
11
+ *
12
+ * @template OwnMessages - 翻訳に使用するメッセージの型。デフォルトは `Messages` です。
13
+ * @returns メッセージを整形する`formatMessage`関数を含むオブジェクトを返します。
14
+ *
15
+ * @example
16
+ * const intl = useNextIntl<MyMessages>();
17
+ * const greeting = intl.formatMessage({ id: 'greeting' });
18
+ */
19
+ var useNextIntl = function () { return (0, useIntlImpl_1.useIntlImpl)(next_intl_1.useTranslations); };
20
+ exports.useNextIntl = useNextIntl;
21
+ //# sourceMappingURL=useNextIntl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNextIntl.js","sourceRoot":"","sources":["../../src/use-intl/useNextIntl.tsx"],"names":[],"mappings":";;;AAAA,uCAA0D;AAE1D,6CAA2C;AAE3C;;;;;;;;;;;;GAYG;AACI,IAAM,WAAW,GAAG,cAA+C,OAAA,IAAA,yBAAW,EAAc,2BAAe,CAAC,EAAzC,CAAyC,CAAA;AAAtG,QAAA,WAAW,eAA2F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smarthr/i18n",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "SmartHR's i18n utility",
5
5
  "author": "SmartHR",
6
6
  "homepage": "https://github.com/kufu/tamatebako/tree/master/packages/i18n",
@@ -25,5 +25,18 @@
25
25
  "prebuild": "pnpm clean",
26
26
  "clean": "rimraf lib"
27
27
  },
28
- "gitHead": "18fc771c7e65e784d224990f2ce857c150cd69e2"
28
+ "peerDependencies": {
29
+ "next": "^14.0.0 || ^15.0.0",
30
+ "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@testing-library/dom": "^10.4.0",
34
+ "@testing-library/react": "^16.3.0",
35
+ "@types/react": "^19.1.9",
36
+ "jest-environment-jsdom": "^30.0.5",
37
+ "next-intl": "^4.3.4",
38
+ "react": "^19.1.0",
39
+ "use-intl": "^4.3.4"
40
+ },
41
+ "gitHead": "cf84f56f02e0c316823e44a0519739d9569e5dc5"
29
42
  }