⛩️

【Flutter】Googleスプレッドシートを使った自動生成型多言語化パッケージを作った

2022/10/21に公開

こんにちは。広瀬マサルです。

みなさん多言語化ってどうしてますか?

僕もいろいろなフレームワークで様々な多言語化パッケージを使ってきましたがどうもイマイチな結果になることが多かったです。

なので、自分で新しいFlutterパッケージを作ってみました。

一言で簡単に言うと

Googleスプレッドシートから自動でコード生成し直感的に使える多言語化パッケージ

です。

翻訳を行うにあたって非常に便利なGoogleスプレッドシートからbuild_runnerを使ってコードジェネレーションを行いタイプセーフに翻訳済みのテキストを入力できます。

テキストの入力も非常に直感的に使えるようにしているので、もしかしたらこれまでの多言語化パッケージでは味わえなかった文字入力のスムーズさを出せるかもしれません。(あくまで可能性です)

使い方をまとめたので興味ある方はぜひ使ってみてください!

katana_localization

https://pub.dev/packages/katana_localization

https://pub.dev/packages/katana_localization_builder

はじめに

多言語化パッケージは、基本的に下記のような特徴を備えているかと思います。

  • 特定のキーとそれに対応した翻訳テキスト定義したファイルを作成
  • 実装時に特定のキーを指定して現在のロケールにおける一致したテキストを抽出して表示

このとき、僕は下記の現象に悩まされてました。

  • 実装時にどのキーが定義されているかが定義ファイルを毎回確認する必要がある

    • どのキーが定義されているかがわからないので違うキーで重複して同じ翻訳テキストを定義することがあった
  • キーをタイポして翻訳が正常に行われないことがある

  • 翻訳をお願いするにあたってファイルを共有して構造を説明するのが面倒

  • 翻訳を自動翻訳で済ませたい場合があるがそれを行うのも面倒

  • BuildContextを使わないとアクセスできない場合がある

  • 翻訳テキスト中に変数を入れたい場合に大抵の場合、後から値を注入する形になるので分かりづらい。

    print(
      sprintf(localize("%s of the %s, by the %s, for the %s"), ["Government", "people", "people", "people"])
    );
    

なので上記を解消するため下記の機能を有するパッケージを作成しました。

  • Googleスプレッドシートで翻訳データを作成する

    • 翻訳をお願いするときにシートを共有するだけで良い
    • キーと翻訳テキストが同一行にあるので、キーとテキストの対応がわかりやすい。そのため翻訳を依頼するときに説明が不要
    • GOOGLETRANSLATE関数を用いることで簡単に自動翻訳が可能
  • build_runnerを使ってコードジェネレーションを行う

    • IDEのサジェスト機能により用意されているキーを確認することができる
    • タイプセーフで入力できるのでタイポしない
  • 変数を利用する場合メソッドチェーンを利用することで文章の順番を崩さずに変数の入力をすることが可能

    • 下記のように書けます。
    print(
      l().$("Goverment").ofThe.$("People").byThe.$("People").forThe.$("People")
    );
    
  • 言語を変更が容易。変更を検知しアプリ自体の描画を再更新できる。

  • BuildContextを使わずともどこからでもアクセスが可能。

インストール

build_runnerを用いたコードジェネレーションを行うため下記のパッケージをインポートします。

flutter pub add katana_localization
flutter pub add --dev build_runner
flutter pub add --dev katana_localization_builder

事前準備

事前にGoogleスプレッドシートを利用可能にします。

  1. こちらのテンプレートからスプレッドシートを自分のGoogleドライブにコピーします。
    • ファイル -> コピーの作成からコピーが可能です。
  2. コピーしたスプレッドシート内でファイル -> 共有 -> 他のユーザーと共有をクリックします。
  3. (作成したスプレッドシート名)を共有ウィンドウ内で、一般的なアクセスリンクを知っている全員に変更します。

Googleスプレッドシートの記述方法

Googleスプレッドシートは一番左の列がキーとなり、各行のキーに対応する翻訳テキストを2行目にあるロケールに従い記載します。

Untitled

キーのプレフィックスに#をつけるとその行は読み込まれません。コメント等にお使いください。

変数を用いる場合

キーに{変数名}を入力することでそこに変数を埋め込むことができます。

翻訳テキストにも同じ{変数名}を記載することで同じ変数名に対応する値が埋め込まれて表示されます。

Untitled

実装

定義ファイル作成

localization.dartなどのDartファイルを作成します。

part ‘元のファイル名.localize.dart’;でPartファイルをインポートします。

そこにGoogleSpreadSheetLocalizeのアノテーションを付与したクラスを作成します。

クラス名は自由ですが必ず_$(定義したクラス名)をextendsしてください。

GoogleSpreadSheetLocalizeのパラメーターには上で用意したGoogleスプレッドシートのURL(例:https://docs.google.com/spreadsheets/d/1bw7IXEr7BGkZ4U6on0OuF7HQkTMgDSm6u5ThpBkDPeo/edit#gid=551986808)をそのままコピーして貼り付けてください。

配列として複数のURLを貼り付けることができます。その場合すべてのスプレッドシートのデータが適用されます。(後に読み込まれたデータが優先)

基本的な翻訳をベースのスプレッドシートとして指定し、その上からアプリごとに異なる翻訳データを追加で指定することが可能です。

通常はダウンロードしたスプレッドシートの内容はキャッシュされますが、versionをインクリメントすることにより新しい内容に更新することができます。

さらにそのクラスを利用するためのトップレベルのフィールドを定義します。フィールド名は短いほうが後で利用しやすくなります。

// localization.dart
import 'package:katana_localization/katana_localization.dart';

part ‘localization.localize.dart’;

(
  [
    "https://docs.google.com/spreadsheets/d/1bw7IXEr7BGkZ4U6on0OuF7HQkTMgDSm6u5ThpBkDPeo/edit#gid=551986808",
  ],
  version: 1,
)
class AppLocalize extends _$AppLocalize { }

final l = AppLocalize();

MaterialAppへの翻訳データの登録

上記で作成したAppLocalizeクラスのオブジェクトを利用してLocalizeScopeMaterialAppを定義します。

LocalizeScopeのlocalizeに先程作成したAppLocalizeのオブジェクトを渡し、builderMaterialAppを配置します。

MaterialAppのlocalelocalizationsDelegatessupportedLocaleslocaleResolutionCallbackAppLocalizeのメソッドやフィールドを渡してください。

// main.dart

import 'package:flutter/material.dart';
import 'package:katana_localization/katana_localization.dart';

import 'localization.dart';

void main() {
  runApp(const MainPage());
}

class MainPage extends StatelessWidget {
  const MainPage({super.key});

  
  Widget build(BuildContext context) {
    return LocalizeScope(
      localize: l,
      builder: (context, localize) {
        return MaterialApp(
          locale: localize.locale,
          localizationsDelegates: localize.delegates(),
          supportedLocales: localize.supportedLocales(),
          localeResolutionCallback: localize.localeResolutionCallback(),
          title: "Test App",
        );
      },
    );
  }
}

コードジェネレーション

下記のコマンドを入力することで自動でコード生成を行います。

flutter pub run build_runner build --delete-conflicting-outputs

連続でコマンドを実行する場合、キャッシュが残って翻訳が反映されないことがあるのでその際は事前に下記のコマンドを入力してください。

flutter pub run build_runner clean

利用方法

翻訳テキストの取得

AppLocalizeのオブジェクトをメソッドとして実行することで翻訳オブジェクトを取得することができます。

翻訳オブジェクトには、Googleスプレッドシートで定義されたキーがそのままフィールドやメソッドとして定義されているため下記のようにして翻訳テキストを取得することができます。

print( l().user ); // English: User、日本語: ユーザー

変数を利用する場合は$(入力値)のメソッドが用意されているのでそこに値を入力します。

また、その入力値にも翻訳オブジェクトを指定することが可能です。

print( l().$( l().saving ).hasBeenCompleted );
// English: Saving has been completed.
// 日本語: 保存が完了しました。

print(
  l().$(
    l().$( l().saving ).of.$( l().data )
  ).hasBeenCompleted
);
// English: Saving of Data has been completed.
// 日本語: データの保存が完了しました。

現在の言語の取得と変更

現在の言語の取得はAppLocalizeのlocaleプロパティで可能です。

また、言語の変更はAppLocalizeのsetCurrentLocaleメソッドで可能です。

Googleスプレッドシートで定義したロケールで利用可能な言語のみに変更されます。

それ以外は変更されません。

setCurrentLocaleが実行されロケールの変更に成功した場合、LocalizeScopeが再ビルドされます。また、AppLocalize自体がChangeNotifierであるためaddListenerで変更を監視することでその他の場所でも変更を検知した処理を実装することが可能です。

おわりに

自分で使う用途で作ったものですが実装の思想的に合ってそうならぜひぜひ使ってみてください!

また、こちらにソースを公開しているのでissueやPullRequestをお待ちしてます!

また仕事の依頼等ございましたら、私のTwitterWebサイトで直接ご連絡をお願いいたします!

https://mathru.net/ja/contact

GitHub Sponsors

スポンサーを随時募集してます。ご支援お待ちしております!

https://github.com/sponsors/mathrunet

Discussion