mcpField
F
flutter-setup-localization

flutter-setup-localization

github.com/dhruvanbhalara開発3056 回インストール9 週間前に更新
リポジトリ

flutter-setup-localization について

Flutter 開発者向けのローカライズ設定スキルです。ARB ファイルとコード生成による多言語テキスト管理に加え、ICU メッセージ形式、RTL レイアウト、導入手順を説明します。

主な機能

  1. 01Flutter のローカライズ依存関係の設定
  2. 02ARB ファイルとメタデータの整理
  3. 03プレースホルダー、複数形、選択形式の処理
  4. 04RTL 方向へのレイアウト対応
  5. 05ローカライズコードの生成と組み込み

活用シーン

  1. 01Flutter アプリへの多言語対応の追加
  2. 02引数や複数形を含む翻訳の管理
  3. 03アラビア語やヘブライ語向け画面の調整
  4. 04ローカライズ設定とコード生成の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dhruvanbhalara/skills --skill flutter-setup-localization

インストール後、Claude Code で/flutter-setup-localizationを入力して読み込まれたか確認します。

詳細

目次

セットアップと設定

Flutter は flutter_localizations パッケージと intl パッケージを使用して国際化を処理します。標準的なワークフローでは、.arb ソースファイルを合成生成パッケージにコンパイルし、型安全なコードアクセスを実現します。

1. 依存関係の追加

ターミナルで以下のコマンドを実行し、必要な依存関係を pubspec.yaml に追加します:

flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any
2. コード生成の有効化

pubspec.yaml ファイルの flutter セクション内で generate フラグを有効にします:

flutter:
  generate: true
3. 設定ファイルの作成

プロジェクトのルートに l10n.yaml ファイルを作成し、ローカライズされたディレクトリの入出力を定義します:

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
synthetic-package: true
4. アプリケーションのエントリポイントの設定

生成された合成パッケージをインポートし、デリゲートとサポート対象のロケールを MaterialApp または CupertinoApp に注入します:

import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

// ... inside the MaterialApp build method
return MaterialApp(
  localizationsDelegates: const [
    AppLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: const [
    Locale('en'), // English
    Locale('es'), // Spanish
  ],
  home: const HomeScreen(),
);

ARB ファイルの構造とルール

App Resource Bundle(.arb)ファイルは、標準的な JSON フォーマットを使用してキーと値の翻訳文字列を表現します。

  • ファイル命名:すべての ARB ファイルは lib/l10n/ 内に app_{locale}.arb パターン(例:app_en.arb、app_es.arb)で配置します。
  • メタデータの要件:テンプレートファイル(app_en.arb)には、すべての翻訳キーに対応する @key メタデータオブジェクトを含める必要があり、翻訳ワークフローを支援する明確な説明を提供します。
  • キー命名規則:camelCase または snake_case を一貫して使用します(例:loginTitle または login_title)。文字列がどこで使用されるかを示す、クリーンで説明的な名前を維持します。
  • ユーザー向け文字列の厳格な分離:プレゼンテーション層でユーザー向け文字列をハードコーディングすることを禁止します。すべてのテキストは ARB キーを通じて公開します。

ICU メッセージフォーマット

ARB ファイル内で ICU メッセージ構文を使用し、動的引数、数量ベースの複数形、条件付き選択を処理します。

1. シンプルなプレースホルダー

パラメータ名を波括弧内で定義し、その型をキーメタデータ内に記述します:

"helloUser": "Hello {name}",
"@helloUser": {
  "description": "Greeting message containing user name",
  "placeholders": {
    "name": {
      "type": "String",
      "example": "Jane"
    }
  }
}
2. 複数形

複数形構文を使用して、数えられる項目のバリエーションを定義します。other ケースは厳密に必須です:

"itemsCount": "{count, plural, =0{no items} =1{1 item} other{{count} items}}",
"@itemsCount": {
  "description": "Inventory count message",
  "placeholders": {
    "count": {
      "type": "num"
    }
  }
}
3. 選択

select 構文を使用して、引数の一致(例:enum のような値)に応じて文字列を表示します:

"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
  "description": "Gendered pronoun selector",
  "placeholders": {
    "gender": {
      "type": "String"
    }
  }
}

RTL(右から左)レイアウトのサポート

RTL 言語(例:アラビア語、ヘブライ語)向けにローカライズする場合、レイアウトは自動的に適応する必要があります:

  • 方向性のある UI:Directionality と、書き込み方向を自動的に解決するウィジェットを使用します。
  • 方向性のあるスペーシング:左右の絶対座標を決して使用しないでください。常に方向性のある同等のマージンとパディングを優先します(例:EdgeInsets.only(left: 16) の代わりに EdgeInsetsDirectional.only(start: 16))。
  • Flex 配置:MainAxisAlignment および CrossAxisAlignment の flex レイアウト設定では、絶対的な left や right の値ではなく、start と end を優先します。

ワークフロー: ローカライズ対応の追加

多言語対応を実装および拡張するには、次のチェックリストに従ってください:

  • パッケージを追加: flutter_localizations パッケージと intl パッケージをインストールします。
  • 生成を有効化: pubspec.yaml の generate: true フラグをオンにします。
  • l10n を設定: プロジェクトのルートに l10n.yaml を作成します。
  • テンプレートを作成: ベースラインとなるキーと説明を含む lib/l10n/app_en.arb を作成します。
  • 対象の翻訳を追加: 翻訳済みの文字列を含む lib/l10n/app_es.arb(およびその他の対象言語ファイル)を作成します。
  • コードを生成: flutter pub get または flutter gen-l10n を実行してコンパイルをトリガーします。
  • Delegate を注入: MaterialApp を更新してローカライズ delegate を定義します。
  • context ショートカットを公開: ウィジェット内で文字列をクリーンに解決するための、簡潔な context 拡張ヘルパーを追加します:
    extension LocalizedContext on BuildContext {
      AppLocalizations get l10n => AppLocalizations.of(this)!;
    }
    
  • レイアウトの境界をテスト: 対象の RTL/LTR ロケールでテストを実行し、レイアウトと配置がエレガントに保たれることを確認します。

例

完全なベース ARB テンプレート (lib/l10n/app_en.arb)
{
  "loginHeader": "Welcome Back",
  "@loginHeader": {
    "description": "Main header visible on the authentication screen"
  },
  "greetingMessage": "Welcome, {username}!",
  "@greetingMessage": {
    "description": "Personalized welcome greeting",
    "placeholders": {
      "username": {
        "type": "String",
        "example": "Jane"
      }
    }
  },
  "cartItems": "{count, plural, =0{Your cart is empty} =1{1 item in your cart} other{{count} items in your cart}}",
  "@cartItems": {
    "description": "Cart items indicator showing quantity status",
    "placeholders": {
      "count": {
        "type": "num"
      }
    }
  }
}
context コンシューマーウィジェット

この例では、カスタムの BuildContext 拡張ヘルパーを使用して、ローカライズされた値をクリーンに利用します。

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

// 1. Clean BuildContext extension helper
extension LocalizedContext on BuildContext {
  AppLocalizations get l10n => AppLocalizations.of(this)!;
}

class DashboardHeader extends StatelessWidget {
  final String username;
  final int itemsCount;

  const DashboardHeader({
    super.key,
    required this.username,
    required this.itemsCount,
  });

  @override
  Widget build(BuildContext context) {
    // 2. Consume values cleanly via the extension shortcut
    final l10n = context.l10n;

    return Padding(
      padding: const EdgeInsetsDirectional.only(start: 16.0, end: 16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            l10n.loginHeader,
            style: Theme.of(context).textTheme.headlineMedium,
          ),
          const SizedBox(height: 8),
          Text(
            l10n.greetingMessage(username),
            style: Theme.of(context).textTheme.bodyLarge,
          ),
          const SizedBox(height: 12),
          Text(
            l10n.cartItems(itemsCount),
            style: Theme.of(context).textTheme.bodyMedium,
          ),
        ],
      ),
    );
  }
}

コメント