ソースコード source code

下記アプリの主要なソースコードを公開しています。アプリ開発の参考になれば幸いです。

サイコロ

Dice

サイコロです。Androidネイティブ(kotlin)で作成したアプリをリニューアルしました。動作が滑らかになって効果音も付加しました。

画像等が別途必要ですので下記情報のみでアプリが完成するものではありません。 アプリは少しずつ機能拡張していますのでストア公開されているアプリと内容が異なる場合があります。 コードはコピーして自由にお使いいただけます。ただし著作権は放棄しておりませんので全部の再掲載はご遠慮ください。部分的に再掲載したり、改変して再掲載するのは構いません。 自身のアプリ作成の参考として個人使用・商用問わず自由にお使いいただけます。 コード記述のお手本を示すものではありません。ミニアプリですので変数名などさほど気遣いしていない部分も有りますし間違いも有るかと思いますので参考程度にお考え下さい。 他の賢者の皆様が公開されているコードを参考にした箇所も含まれます。Flutter開発の熟練者が書いたコードではありません。 エンジニア向け技術情報共有サービスではありませんので説明は省いています。 GitHubなどへの公開は予定しておりません。

下記コードの最終ビルド日: 2025-02-26

pubspec.yaml

name: dice
description: "Dice"
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 2.3.1+22

environment:
  sdk: ^3.6.0-198.0.dev

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.8
  package_info_plus: ^8.3.0
  shared_preferences: ^2.0.17
  flutter_localizations:    #多言語ライブラリの本体    # .arbファイルを更新したら flutter gen-l10n
    sdk: flutter
  intl: ^0.20.2     #多言語やフォーマッタなどの関連ライブラリ
  google_mobile_ads: ^5.3.1
  just_audio: ^0.9.35
  video_player: ^2.9.3

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_launcher_icons: ^0.14.3    #flutter pub run flutter_launcher_icons
  flutter_native_splash: ^2.4.5     #flutter pub run flutter_native_splash:create

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^5.0.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

flutter_launcher_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/icon/icon.png"
  adaptive_icon_background: "assets/icon/icon_back.png"
  adaptive_icon_foreground: "assets/icon/icon_fore.png"

flutter_native_splash:
  color: '#006600'
  image: 'assets/image/splash.png'
  color_dark: '#006600'
  image_dark: 'assets/image/splash.png'
  fullscreen: true
  android_12:
    icon_background_color: '#006600'
    image: 'assets/image/splash.png'
    icon_background_color_dark: '#006600'
    image_dark: 'assets/image/splash.png'

# The following section is specific to Flutter packages.
flutter:
  generate: true    #自動生成フラグの有効化

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg
  assets:
    - assets/image/
    - assets/sound/

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/to/resolution-aware-images

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/to/asset-from-package

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/to/font-from-package

lib/ad_mob.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-02-06
///
library;

import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io';

class AdMob {
  late BannerAd _adMobBanner;
  bool _isAdMob = false;
  AdMob() { //constructor
    String adBannerUnitId = '';
    if (!kIsWeb && Platform.isAndroid) {
      //adBannerUnitId = 'ca-app-pub-3940256099942544/6300978111';  //test
      adBannerUnitId = 'ca-app-pub-0000000000000000/0000000000';
      _isAdMob = true;
    } else if (!kIsWeb && Platform.isIOS) {
      //adBannerUnitId = 'ca-app-pub-3940256099942544/6300978111';  //test
      adBannerUnitId = 'ca-app-pub-0000000000000000/0000000000';
      _isAdMob = true;
    }
    if (_isAdMob) {
      _adMobBanner = BannerAd(
        adUnitId: adBannerUnitId,
        size: AdSize.banner,
        request: const AdRequest(),
        listener: const BannerAdListener(),
      );
    }
  }
  void load() {
    if (_isAdMob) {
      _adMobBanner.load();
    }
  }
  void dispose() {
    if (_isAdMob) {
      _adMobBanner.dispose();
    }
  }
  Widget getAdBannerWidget() {
    if (_isAdMob) {
      return Container(
        alignment: Alignment.center,
        width: _adMobBanner.size.width.toDouble(),
        height: _adMobBanner.size.height.toDouble(),
        child: AdWidget(ad: _adMobBanner),
      );
    } else {
      return Container();
    }
  }
  double getAdBannerHeight() {
    if (_isAdMob) {
      return _adMobBanner.size.height.toDouble();
    } else {
      return 150;  //for web
    }
  }
}

lib/audio_play.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-01-27
///
library;

import 'package:just_audio/just_audio.dart';

import 'package:dice/const_value.dart';

class AudioPlay {
  //音を重ねて連続再生できるようにインスタンスを用意しておき、順繰りに使う。
  static final List<AudioPlayer> _player01 = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  static final List<AudioPlayer> _player02 = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  int _player01Ptr = 0;
  int _player02Ptr = 0;

  double _soundVolume01 = 0.0;
  double _soundVolume02 = 0.0;

  //constructor
  AudioPlay() {
    constructor();
  }
  void constructor() async {
    for (int i = 0; i < _player01.length; i++) {
      await _player01[i].setVolume(0);
      await _player01[i].setAsset(ConstValue.audioSwitch);
    }
    for (int i = 0; i < _player02.length; i++) {
      await _player02[i].setVolume(0);
      await _player02[i].setAsset(ConstValue.audioDice);
    }
    playZero();
  }
  void dispose() {
    for (int i = 0; i < _player01.length; i++) {
      _player01[i].dispose();
    }
    for (int i = 0; i < _player02.length; i++) {
      _player02[i].dispose();
    }
  }
  //getter
  double get soundVolume01 {
    return _soundVolume01;
  }
  double get soundVolume02 {
    return _soundVolume02;
  }
  //setter
  set soundVolume01(double vol) {
    _soundVolume01 = vol;
  }
  set soundVolume02(double vol) {
    _soundVolume02 = vol;
  }
  //最初に音が鳴らないのを回避する方法
  void playZero() async {
    AudioPlayer ap = AudioPlayer();
    await ap.setAsset(ConstValue.audioZero);
    await ap.load();
    await ap.play();
  }
  //
  void play01() async {
    if (_soundVolume01 == 0) {
      return;
    }
    _player01Ptr += 1;
    if (_player01Ptr >= _player01.length) {
      _player01Ptr = 0;
    }
    await _player01[_player01Ptr].setVolume(_soundVolume01);
    await _player01[_player01Ptr].pause();
    await _player01[_player01Ptr].seek(const Duration(milliseconds: 100));
    await _player01[_player01Ptr].play();
  }
  //
  void play02() async {
    if (_soundVolume02 == 0) {
      return;
    }
    _player02Ptr += 1;
    if (_player02Ptr >= _player02.length) {
      _player02Ptr = 0;
    }
    await _player02[_player02Ptr].setVolume(_soundVolume02);
    await _player02[_player02Ptr].pause();
    await _player02[_player02Ptr].seek(Duration.zero);
    await _player02[_player02Ptr].play();
  }
}

lib/const_value.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-15
///
library;

import 'package:flutter/material.dart';

class ConstValue {
  //pref
  static const String prefLanguageCode = 'languageCode';
  static const String prefObjectNumber = 'objectNumber';
  static const String prefDiceCount = 'diceCount';
  static const String prefCountdownTime = 'countdownTime';
  static const String prefSoundThrowVolume = 'soundThrowVolume';
  static const String prefSoundRollingVolume = 'soundRollingVolume';
  //color
  static const Color colorHeader = Color.fromRGBO(0,80,0,1);
  static const Color colorHeaderOn = Color.fromRGBO(0,0,0,0.5);
  static const Color colorBack = Color.fromRGBO(0,102,0,1);
  static const Color colorSettingHeader = Color.fromRGBO(0,102,0,1);
  static const Color colorUiActiveColor = Color.fromRGBO(0,150,0,1);
  static const Color colorUiInactiveColor = Colors.black26;
  //sound
  static const String audioZero = 'assets/sound/zero.wav';    //無音1秒
  static const String audioSwitch = 'assets/sound/switch.wav';
  static const String audioDice = 'assets/sound/dice.wav';
  //movie
  static const List<List<String>> videoObjects = [
    [
      'assets/image/dice1.mp4',
      'assets/image/dice2.mp4',
      'assets/image/dice3.mp4',
      'assets/image/dice4.mp4',
      'assets/image/dice5.mp4',
      'assets/image/dice6.mp4',
    ],
    [
      'assets/image/coin1.mp4',
      'assets/image/coin2.mp4',
      'assets/image/coin1.mp4',
      'assets/image/coin2.mp4',
      'assets/image/coin1.mp4',
      'assets/image/coin2.mp4',
    ],
    [
      'assets/image/geta1.mp4',
      'assets/image/geta2.mp4',
      'assets/image/geta3.mp4',
      'assets/image/geta1.mp4',
      'assets/image/geta2.mp4',
      'assets/image/geta3.mp4',
    ],
    [
      'assets/image/koma1.mp4',
      'assets/image/koma2.mp4',
      'assets/image/koma1.mp4',
      'assets/image/koma2.mp4',
      'assets/image/koma1.mp4',
      'assets/image/koma2.mp4',
    ],
    [
      'assets/image/janken1.mp4',
      'assets/image/janken2.mp4',
      'assets/image/janken3.mp4',
      'assets/image/janken4.mp4',
      'assets/image/janken5.mp4',
      'assets/image/janken6.mp4',
    ],
  ];
  //image
  static const List<String> imageNumbers = [
    'assets/image/number_null.webp',
    'assets/image/number1.webp',
    'assets/image/number2.webp',
    'assets/image/number3.webp',
    'assets/image/number4.webp',
    'assets/image/number5.webp',
    'assets/image/number6.webp',
    'assets/image/number7.webp',
    'assets/image/number8.webp',
    'assets/image/number9.webp',
  ];
  //string
  static const Map<String,String> languageCode = {
    'en': 'English',
    'bg': 'български език',
    'cs': 'Čeština',
    'da': 'dansk',
    'de': 'Deutsch',
    'el': 'Ελληνικά',
    'es': 'Español',
    'et': 'eesti keel',
    'fi': 'Suomen kieli',
    'fr': 'Français',
    'hu': 'magyar nyelv',
    'it': 'Italiano',
    'ja': '日本語',
    'lt': 'lietuvių kalba',
    'lv': 'Latviešu',
    'nl': 'Nederlands',
    'pl': 'Polski',
    'pt': 'Português',
    'ro': 'limba română',
    'ru': 'русский',
    'sk': 'Slovenčina',
    'sv': 'svenska',
    'th': 'ภาษาไทย',
    'zh': '中文',
  };

}

lib/empty.dart

lib/language_state.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-01-27
///
library;

import 'package:dice/preferences.dart';

class LanguageState {

  static String _languageCode = 'en';

  //言語コードを記録
  static Future<void> setLanguageCode(String str) async {
    _languageCode = str;
    await Preferences.setLanguageCode(_languageCode);
  }
  //言語コードを返す
  static Future<String> getLanguageCode() async {
    _languageCode = await Preferences.getLanguageCode() ?? 'en';
    return _languageCode;
  }

}

lib/main.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-02
///
library;

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart' if (dart.library.html) 'empty.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:video_player/video_player.dart';

//自身で作成したclassを読み込む
import 'package:dice/const_value.dart';
import 'package:dice/language_state.dart';
import 'package:dice/version_state.dart';
import 'package:dice/setting.dart';
import 'package:dice/ad_mob.dart';
import 'package:dice/preferences.dart';
import 'package:dice/audio_play.dart';

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

class MainApp extends StatefulWidget {    //statefulに変更して言語変更に対応
  const MainApp({super.key});
  @override
  State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  Locale localeLanguage = const Locale('en');
  @override
  Widget build(BuildContext context) {
    if (kIsWeb == false) {
      MobileAds.instance.initialize();
    }
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      localizationsDelegates: AppLocalizations.localizationsDelegates,   //多言語化
      supportedLocales: AppLocalizations.supportedLocales,  //自動で言語リストを生成
      locale: localeLanguage,
      home: const MainHomePage(),
    );
  }
}

class MainHomePage extends StatefulWidget {
  const MainHomePage({super.key});
  @override
  State<MainHomePage> createState() => _MainHomePageState();
}

class _MainHomePageState extends State<MainHomePage> with SingleTickerProviderStateMixin {
  final AdMob _adMob = AdMob(); //広告表示
  final AudioPlay _audioPlay = AudioPlay(); //効果音
  late Random _random;
  bool _busyFlag = false; //動作中
  //dice回転
  late AnimationController _animationController;
  late final List<Animation<double>?> _diceRotateAnimation = [
    null,null,null,null,null,null,
  ];
  final List<int> _rotateIndex = [0,1,2,3,4,5]; //shuffleされて使用される
  //dice動画
  final List<VideoPlayerController?> _videoPlayerController = [
    null,null,null,null,null,null,
  ];
  final List<int> _videoIndex = [0,0,0,0,0,0];
  //countdown
  int _countdownSubtraction = 0;  //_countdownTimeが代入されて実際にカウントダウンされる
  String _imageCountdownNumber = ConstValue.imageNumbers[0];  //カウントダウン画像
  double _countdownScale = 3;   //カウントダウン画像の拡大率
  double _countdownOpacity = 0; //カウントダウン画像の非透明度
  int _timerCount = 30; //Timerで処理される1秒間の数
  late Timer _timer;
  //

  //アプリのバージョン取得
  void _getVersion() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    setState(() {
      VersionState.versionSave(packageInfo.version);
    });
  }
  //言語準備
  void _getCurrentLocale() async {
    Locale locale = Locale(await LanguageState.getLanguageCode());
    if (mounted) {  //Widgetが存在する。Widgetが存在しない時の実行によるエラーを回避する為。
      context.findAncestorStateOfType<_MainAppState>()!
        ..localeLanguage = locale
        ..setState(() {});
    }
  }
  //ページ起動開始時に一度だけ呼ばれる
  @override
  void initState() {
    super.initState();
    _getVersion();
    _getCurrentLocale();
    LanguageState.getLanguageCode();
    _adMob.load();
    _audioPlay.playZero();
    _random = Random(DateTime.now().millisecondsSinceEpoch);
    //animation
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 2000),
    );
    for (int i = 0; i < _diceRotateAnimation.length; i++) {
      double angle = (_random.nextDouble() * 3.14 * 4) - (3.14 * 2);
      _diceRotateAnimation[i] = Tween<double>(begin: 0, end: angle).animate(_animationController);
    }
    _animationController.addListener(() {
      setState(() {});
    });
    //
    Future<void>(() async {
      await Preferences.initial();
      _audioPlay.soundVolume01 = Preferences.soundThrowVolume;
      _audioPlay.soundVolume02 = Preferences.soundRollingVolume;
      _readyMovie();
      setState(() {});
    });
  }
  //ページ終了時に一度だけ呼ばれる
  @override
  void dispose() {
    _adMob.dispose();
    _timer.cancel();
    for (int i = 0; i < _videoPlayerController.length; i++) {
      _videoPlayerController[i]?.dispose();
    }
    super.dispose();
  }
  void _readyMovie() {
    for (int i = 0; i < _videoPlayerController.length; i++) {
      if (_videoPlayerController[i] != null) {
        _videoPlayerController[i]?.dispose();
      }
    }
    for (int i = 0; i < _videoPlayerController.length; i++) {
      _videoPlayerController[i] = VideoPlayerController.asset(ConstValue.videoObjects[Preferences.objectNumber][i],
        videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),  //複数の動画を処理
      );
      _videoPlayerController[i]?.addListener(() {
        setState(() {});
      });
      _videoPlayerController[i]?.setLooping(false);
      _videoPlayerController[i]?.initialize().then((_) {
        setState((){});
      });
    }
  }
  //画面全体
  @override
  Widget build(BuildContext context) {
    if (_videoPlayerController[_videoPlayerController.length - 1] == null) {  //準備待ち
      return Container();
    }
    return GestureDetector(
      onTap: () {
        _onClickStart();
      },
      child: Scaffold(
        backgroundColor: ConstValue.colorBack,
        appBar: AppBar(
          backgroundColor: _busyFlag ? ConstValue.colorHeaderOn : ConstValue.colorHeader,
          //タイトル表示
          title: const Text('Dice',
            style: TextStyle(
              color: Colors.white,
              fontSize: 15.0,
            )
          ),
          //設定ボタン
          actions: <Widget>[
            Opacity(
              opacity: _busyFlag ? 0.1 : 1,
              child: TextButton(
                onPressed: () async {
                  if (_busyFlag) {
                    return;
                  }
                  bool? ret = await Navigator.of(context).push(
                    MaterialPageRoute<bool>(builder:(context) => const SettingPage()),
                  );
                  //awaitで呼び出しているので、settingから戻ったら以下が実行される。
                  if (ret!) { //設定で適用だった場合
                    _getCurrentLocale();
                    _audioPlay.soundVolume01 = Preferences.soundThrowVolume;
                    _audioPlay.soundVolume02 = Preferences.soundRollingVolume;
                    _readyMovie();
                    setState(() {});
                  }
                },
                child: Text(
                  AppLocalizations.of(context)!.setting,
                  style: const TextStyle(
                    color: Colors.white,
                  )
                )
              )
            )
          ]
        ),
        body: SafeArea(
          child: Column(children:[
            SizedBox(
              width: double.infinity,
              child: Text(AppLocalizations.of(context)!.start,
                textAlign: TextAlign.center,
              )
            ),
            Expanded(
              child: Stack(children:[
                Center(
                  child: _diceArea(),
                ),
                Center(
                  child: Opacity(
                    opacity: _countdownOpacity,
                    child: Transform.scale(
                      scale: _countdownScale,
                      child: Image.asset(
                        _imageCountdownNumber,
                      ),
                    )
                  )
                ),
              ])
            ),
            //広告
            Padding(
              padding: const EdgeInsets.only(top: 10, left: 0, right: 0, bottom: 0),
              child: SizedBox(
                width: double.infinity,
                child: _adMob.getAdBannerWidget(),
              )
            )
          ])
        )
      )
    );
  }
  //カウントダウンタイマー
  void _timerStart() {
    _timer = Timer.periodic(const Duration(milliseconds: (1000 ~/ 30)), (timer) {
      setState(() {
        _countdown();
      });
    });
  }
  //START
  void _onClickStart() {
    if (_busyFlag) {
      return;
    }
    _busyFlag = true;
    _countdownSubtraction = Preferences.countdownTime;
    if (_countdownSubtraction == 0) { //カウントダウンしない場合
      _diceAction();
    } else {
      _audioPlay.soundVolume01 = Preferences.soundThrowVolume;
      _audioPlay.play01();
      _timerStart();
    }
  }
  void _diceAction() async {
    //一瞬動画の最後が表示されるので動画を差し替える
    for (int i = 0; i < Preferences.diceCount; i++) {
      _videoIndex[i] = 0;
    }
    //先に全ての動画をawaitで先頭に戻しておくことで動画の最後が一瞬表示されることを防ぐ
    for (int i = 0; i < _videoPlayerController.length; i++) {
      await _videoPlayerController[i]!.seekTo(Duration.zero);
    }
    //
    _audioPlay.soundVolume02 = Preferences.soundRollingVolume;
    _audioPlay.play02();
    _animationController.reset();
    _rotateIndex.shuffle();
    for (int i = 0; i < Preferences.diceCount; i++) {
      _videoIndex[i] = _random.nextInt(6);
      _videoPlayerController[_videoIndex[i]]!.play();
    }
    _animationController.forward();
    Future.delayed(const Duration(milliseconds: 2000)).then((_) => {
      _busyFlag = false,
      setState((){}),
    });
  }
  //Timerで定期実行
  void _countdown() {
    //カウントダウン終了時
    if (_countdownSubtraction == 0) {
      return;
    }
    //数字画像を切り替え
    if (_timerCount == 30) {
      _imageCountdownNumber = ConstValue.imageNumbers[_countdownSubtraction];
    }
    _timerCount -= 1;
    if (_timerCount <= 0) {
      _timerCount = 30;
      _countdownSubtraction -= 1;
      if (_countdownSubtraction == 0) {
        _imageCountdownNumber = ConstValue.imageNumbers[0];
        _timer.cancel();
        _diceAction();
      }
    }
    _countdownScale = 1 + (0.1 * (_timerCount / 30));
    if (_timerCount >= 20) {
      _countdownOpacity = (30 - _timerCount) / 10;
    } else if (_timerCount <= 5) {
      _countdownOpacity = _timerCount / 5;
    } else {
      _countdownOpacity = 1;
    }
  }
  Widget _diceArea() {
    if (Preferences.diceCount == 1) {
      return _objectOne(0);
    } else if (Preferences.diceCount == 2) {
      return AspectRatio(
        aspectRatio: 1/2,
        child: Column(children:[
          Expanded(
            child: _objectOne(0),
          ),
          Expanded(
            child: _objectOne(1),
          ),
        ])
      );
    } else if (Preferences.diceCount == 3) {
      return AspectRatio(
        aspectRatio: 1,
        child: Column(children:[
          Expanded(
            child: Row(children:[
              _objectOneExpanded(0),
              _objectOneExpanded(1),
            ])
          ),
          Expanded(
            child: _objectOne(2),
          ),
        ])
      );
    } else if (Preferences.diceCount == 4) {
      return AspectRatio(
        aspectRatio: 1,
        child: Column(children:[
          Expanded(
            child: Row(children:[
              _objectOneExpanded(0),
              _objectOneExpanded(1),
            ])
          ),
          Expanded(
            child: Row(children:[
              _objectOneExpanded(2),
              _objectOneExpanded(3),
            ])
          ),
        ])
      );
    } else if (Preferences.diceCount == 5) {
      return AspectRatio(
        aspectRatio: 2/3,
        child: Column(children:[
          Expanded(
            child: Row(children:[
              _objectOneExpanded(0),
              _objectOneExpanded(1),
            ])
          ),
          Expanded(
            child: Row(children:[
              _objectOneExpanded(2),
              _objectOneExpanded(3),
            ])
          ),
          Expanded(
            child: _objectOne(4),
          ),
        ])
      );
    } else if (Preferences.diceCount == 6) {
      return AspectRatio(
        aspectRatio: 2/3,
        child: Column(children:[
          Expanded(
            child: Row(children:[
              _objectOneExpanded(0),
              _objectOneExpanded(1),
            ])
          ),
          Expanded(
            child: Row(children:[
              _objectOneExpanded(2),
              _objectOneExpanded(3),
            ])
          ),
          Expanded(
            child: Row(children:[
              _objectOneExpanded(4),
              _objectOneExpanded(5),
            ])
          ),
        ])
      );
    }
    return Container();
  }
  Widget _objectOne(int index) {
    return ClipOval(
      child: Transform.rotate(
        angle: _diceRotateAnimation[_rotateIndex[index]]!.value,
        child: AspectRatio(
          aspectRatio: 1,
          child: VideoPlayer(_videoPlayerController[_videoIndex[index]]!),
        )
      )
    );
  }
  Widget _objectOneExpanded(int index) {
    return Expanded(
      child: Center(
        child: ClipOval(
          child: Transform.rotate(
            angle: _diceRotateAnimation[_rotateIndex[index]]!.value,
            child: AspectRatio(
              aspectRatio: 1,
              child: VideoPlayer(_videoPlayerController[_videoIndex[index]]!),
            )
          )
        )
      )
    );
  }
}

lib/preferences.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-26
///
library;

import 'package:shared_preferences/shared_preferences.dart';

import 'package:dice/const_value.dart';

//デバイスに情報を保存
class Preferences {

  static bool ready = false;
  //この値は常に最新にしておく
  static String _languageCode = '';
  static int _objectNumber = 0;
  static int _diceCount = 1;
  static int _countdownTime = 0;
  static double _soundThrowVolume = 0.5;
  static double _soundRollingVolume = 0.5;

  static String get languageCode {
    return _languageCode;
  }
  static int get objectNumber {
    return _objectNumber;
  }
  static int get diceCount {
    return _diceCount;
  }
  static int get countdownTime {
    return _countdownTime;
  }
  static double get soundThrowVolume {
    return _soundThrowVolume;
  }
  static double get soundRollingVolume {
    return _soundRollingVolume;
  }

  static Future<void> initial() async {
    _languageCode = await getLanguageCode();
    _objectNumber = await getObjectNumber();
    _diceCount = await getDiceCount();
    _countdownTime = await getCountdownTime();
    _soundThrowVolume = await getSoundThrowVolume();
    _soundRollingVolume = await getSoundRollingVolume();
    ready = true;
  }

  //----------------------------

  //言語コード
  static Future<void> setLanguageCode(String str) async {
    _languageCode = str;
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString(ConstValue.prefLanguageCode, str);
  }
  static Future<String> getLanguageCode() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String str = prefs.getString(ConstValue.prefLanguageCode) ?? 'en';
    return str;
  }

  //----------------------------

  //物体番号
  static Future<void> setObjectNumber(int num) async {
    _objectNumber = num;
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setInt(ConstValue.prefObjectNumber, num);
  }
  static Future<int> getObjectNumber() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int num = prefs.getInt(ConstValue.prefObjectNumber) ?? 0;
    return num;
  }

  //----------------------------

  //さいころの数
  static Future<void> setDiceCount(int num) async {
    _diceCount = num;
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setInt(ConstValue.prefDiceCount, num);
  }
  static Future<int> getDiceCount() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int num = prefs.getInt(ConstValue.prefDiceCount) ?? 1;
    return num;
  }

  //----------------------------

  //カウントダウン時間
  static Future<void> setCountdownTime(int num) async {
    _countdownTime = num;
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setInt(ConstValue.prefCountdownTime, num);
  }
  static Future<int> getCountdownTime() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int num = prefs.getInt(ConstValue.prefCountdownTime) ?? 0;
    return num;
  }

  //----------------------------

  //効果音音量
  static Future<void> setSoundThrowVolume(double num) async {
    _soundThrowVolume = num;
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setDouble(ConstValue.prefSoundThrowVolume, num);
  }
  static Future<double> getSoundThrowVolume() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final double num = prefs.getDouble(ConstValue.prefSoundThrowVolume) ?? 0.5;
    return num;
  }

  //----------------------------

  //効果音音量
  static Future<void> setSoundRollingVolume(double num) async {
    _soundRollingVolume = num;
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setDouble(ConstValue.prefSoundRollingVolume, num);
  }
  static Future<double> getSoundRollingVolume() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final double num = prefs.getDouble(ConstValue.prefSoundRollingVolume) ?? 0.5;
    return num;
  }

  //----------------------------

}

lib/setting.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-15
///
library;

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

import 'package:dice/const_value.dart';
import 'package:dice/preferences.dart';
import 'package:dice/language_state.dart';
import 'package:dice/version_state.dart';
import 'package:dice/ad_mob.dart';

class SettingPage extends StatefulWidget {
  const SettingPage({super.key});

  @override
  State<SettingPage> createState() => _SettingPageState();
}

class _SettingPageState extends State<SettingPage> {
  final AdMob _adMob = AdMob(); //広告
  //これら変数はUIへの表示や入力の為に一時的に使用される。
  String _languageKey = ''; //言語コード 'en'
  String _languageValue = '';
  int _objectNumber = 0;
  int _diceCount = 1;
  int _countdownTime = 0;
  double _soundThrowVolume = 0.5;
  double _soundRollingVolume = 0.5;
  late List<String> _objectName;

  //ページ起動時に一度だけ実行される
  @override
  void initState() {
    super.initState();
    _adMob.load();
    Future<void>(() async {
      _objectName = [
        AppLocalizations.of(context)!.objectName0,
        AppLocalizations.of(context)!.objectName1,
        AppLocalizations.of(context)!.objectName2,
        AppLocalizations.of(context)!.objectName3,
        AppLocalizations.of(context)!.objectName4,
      ];
      _languageKey = await LanguageState.getLanguageCode();
      _languageValue = ConstValue.languageCode[_languageKey] ?? '';
      await Preferences.initial();
      _objectNumber = Preferences.objectNumber;
      _diceCount = Preferences.diceCount;
      _countdownTime = Preferences.countdownTime;
      _soundThrowVolume = Preferences.soundThrowVolume;
      _soundRollingVolume = Preferences.soundRollingVolume;
      setState((){});
    });
  }
  //ページ終了時に一度だけ実行される
  @override
  void dispose() {
    _adMob.dispose();
    super.dispose();
  }
  //ページ描画
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        elevation: 0,
        //設定キャンセルボタン
        leading: IconButton(
          icon: const Icon(Icons.close),
          onPressed: () {
            Navigator.of(context).pop(false); //falseを返す
          },
        ),
        title: Text(AppLocalizations.of(context)!.setting),
        foregroundColor: const Color.fromRGBO(255,255,255,1),
        backgroundColor: ConstValue.colorSettingHeader,
        actions: [
          //設定OKボタン
          IconButton(
            icon: const Icon(Icons.check),
            onPressed: () async {
              await LanguageState.setLanguageCode(_languageKey);
              await Preferences.setObjectNumber(_objectNumber);
              await Preferences.setDiceCount(_diceCount);
              await Preferences.setCountdownTime(_countdownTime);
              await Preferences.setSoundThrowVolume(_soundThrowVolume);
              await Preferences.setSoundRollingVolume(_soundRollingVolume);
              if (!mounted) {
                return;
              }
              Navigator.of(context).pop(true);  //trueを返す
            },
          ),
        ],
      ),
      body: SafeArea(
        child: Column(children:[
          Expanded(
            child: GestureDetector(
              onTap: () => FocusScope.of(context).unfocus(),  //背景タップでキーボードを仕舞う
              child: SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.all(20),
                  child: Column(children: [
                    Padding(
                      padding: const EdgeInsets.only(top: 18, left: 0, right: 0, bottom: 0),
                      child: Row(children:[
                        const SizedBox(width:16),
                        Text(AppLocalizations.of(context)!.objectChoice,
                          style: const TextStyle(
                            fontSize: 16,
                          )
                        ),
                        const Spacer(),
                      ])
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 0, right: 0, bottom: 18),
                      child: Column(children:[
                        _objectRadio(0),
                        _objectRadio(1),
                        _objectRadio(2),
                        _objectRadio(3),
                        _objectRadio(4),
                      ])
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 18, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.objectCount,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ])
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 6),
                      child: Row(children: <Widget>[
                        Text(_diceCount.toString()),
                        Expanded(
                          child: Slider(
                            value: _diceCount.toDouble(),
                            min: 1,
                            max: 6,
                            divisions: 5,
                            onChanged: (double value) {
                              setState(() {
                                _diceCount = value.toInt();
                              });
                            },
                            activeColor: ConstValue.colorUiActiveColor,
                            inactiveColor: ConstValue.colorUiInactiveColor,
                          )
                        )
                      ])
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 18, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.countdownTime,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ])
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 6),
                      child: Row(children: <Widget>[
                        Text(_countdownTime.toString()),
                        Expanded(
                          child: Slider(
                            value: _countdownTime.toDouble(),
                            min: 0,
                            max: 9,
                            divisions: 9,
                            onChanged: (double value) {
                              setState(() {
                                _countdownTime = value.toInt();
                              });
                            },
                            activeColor: ConstValue.colorUiActiveColor,
                            inactiveColor: ConstValue.colorUiInactiveColor,
                          )
                        )
                      ])
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 18, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.soundThrowVolume,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ])
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 6),
                      child: Row(children: <Widget>[
                        Text(_soundThrowVolume.toString()),
                        Expanded(
                          child: Slider(
                            value: _soundThrowVolume,
                            min: 0.0,
                            max: 1.0,
                            divisions: 10,
                            onChanged: (double value) {
                              setState(() {
                                _soundThrowVolume = value;
                              });
                            },
                            activeColor: ConstValue.colorUiActiveColor,
                            inactiveColor: ConstValue.colorUiInactiveColor,
                          )
                        )
                      ])
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 18, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.soundRollingVolume,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ])
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 6),
                      child: Row(children: <Widget>[
                        Text(_soundRollingVolume.toString()),
                        Expanded(
                          child: Slider(
                            value: _soundRollingVolume,
                            min: 0.0,
                            max: 1.0,
                            divisions: 10,
                            onChanged: (double value) {
                              setState(() {
                                _soundRollingVolume = value;
                              });
                            },
                            activeColor: ConstValue.colorUiActiveColor,
                            inactiveColor: ConstValue.colorUiInactiveColor,
                          )
                        )
                      ])
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 18, left: 0, right: 0, bottom: 0),
                      child: Row(children:[
                        const SizedBox(width:16),
                        Text(AppLocalizations.of(context)!.language,
                          style: const TextStyle(
                            fontSize: 16,
                          )
                        ),
                        const Spacer(),
                      ])
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 0, right: 0, bottom: 18),
                      child: Table(
                        children: <TableRow>[
                          TableRow(children: <Widget>[
                            _languageTableCell(0),
                            _languageTableCell(1),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(2),
                            _languageTableCell(3),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(4),
                            _languageTableCell(5),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(6),
                            _languageTableCell(7),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(8),
                            _languageTableCell(9),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(10),
                            _languageTableCell(11),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(12),
                            _languageTableCell(13),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(14),
                            _languageTableCell(15),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(16),
                            _languageTableCell(17),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(18),
                            _languageTableCell(19),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(20),
                            _languageTableCell(21),
                          ]),
                          TableRow(children: <Widget>[
                            _languageTableCell(22),
                            _languageTableCell(23),
                          ]),
                        ]
                      ),
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 24, left: 0, right: 0, bottom: 24),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children:[
                          Text(AppLocalizations.of(context)!.usage1),
                          const SizedBox(height:15),
                          Text(AppLocalizations.of(context)!.usage2),
                          const SizedBox(height:15),
                          Text(AppLocalizations.of(context)!.usage3),
                          const SizedBox(height:15),
                          Text(AppLocalizations.of(context)!.usage4),
                        ]
                      ),
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 24, left: 0, right: 0, bottom: 36),
                      child: SizedBox(
                        child: Text('version  ${VersionState.versionLoad()}',
                          style: const TextStyle(fontSize: 10),
                        ),
                      ),
                    ),
                  ]),
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 10, left: 0, right: 0, bottom: 0),
            child: SizedBox(
              width: double.infinity,
              child: _adMob.getAdBannerWidget(),
            ),
          ),
        ]),
      )
    );
  }
  //UIの仕切り用ボーダーライン
  Widget _border() {
    return Container(
      decoration: BoxDecoration(
        border: Border(
          top: BorderSide(
            color: Colors.grey.shade300,
            width: 1,
          ),
        ),
      ),
    );
  }
  //物体
  Widget _objectRadio(int index) {
    return RadioListTile(
      visualDensity: const VisualDensity(horizontal: VisualDensity.minimumDensity,vertical: VisualDensity.minimumDensity),
      contentPadding: EdgeInsets.zero,
      title: Text(_objectName[index]),
      value: index,
      groupValue: _objectNumber,
      onChanged: (int? value) {
        setState(() {
          _objectNumber = value ?? 0;
        });
      },
      activeColor: ConstValue.colorUiActiveColor,
    );
  }
  //言語一覧表示
  TableCell _languageTableCell(int index) {
    return TableCell(
      child: RadioListTile(
        visualDensity: const VisualDensity(horizontal: VisualDensity.minimumDensity,vertical: VisualDensity.minimumDensity),
        contentPadding: EdgeInsets.zero,
        title: Text(ConstValue.languageCode.values.elementAt(index)),
        value: ConstValue.languageCode.values.elementAt(index),
        groupValue: _languageValue,
        onChanged: (String? value) {
          setState(() {
            _languageValue = value ?? '';
            _languageKey = ConstValue.languageCode.keys.elementAt(index);
          });
        },
        activeColor: ConstValue.colorUiActiveColor,
      ),
    );
  }

}

lib/version_state.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-01-27
///
library;

class VersionState {

  static String _version = '';

  //バージョンを記録
  static void versionSave(String str) {
    _version = str;
  }
  //バージョンを返す
  static String versionLoad() {
    return _version;
  }

}

lib/l10n/app_bg.arb

{
    "@@locale":"bg",
    "@locale": {
        "description": "ブルガリア"
    },
    "setting": "Настройка",
    "start": "Започнете с докосване на екрана",
    "objectChoice": "Обект",
    "objectCount": "Брой обекти",
    "countdownTime": "обратно броене",
    "soundThrowVolume": "Сила на звука: обратно броене",
    "soundRollingVolume": "Обем: Обект",
    "objectName0": "Шестстранни зарове",
    "objectName1": "Монета",
    "objectName2": "Дървени сабо",
    "objectName3": "парчета шоги",
    "objectName4": "Камък, ножица, хартия",
    "language": "език",
    "usage1": "Зарове",
    "usage2": "Поддържа от 1 до 6 зара. Заровете се хвърлят чрез докосване на екрана.",
    "usage3": "Може да се настрои времето за обратно броене до хвърлянето на зара. Ако не е необходимо обратно броене, настройте на нула.",
    "usage4": "Силата на звука на звуковите ефекти може да се регулира. Ако звуковият ефект не е необходим, задайте го на нула.",

    "dummy": "dummy"
}

lib/l10n/app_cs.arb

{
    "@@locale":"cs",
    "@locale": {
        "description": "チェコ"
    },
    "setting": "Nastavení",
    "start": "Začněte klepnutím na obrazovku",
    "objectChoice": "Objekt",
    "objectCount": "Počet objektů",
    "countdownTime": "Odpočítávání",
    "soundThrowVolume": "Hlasitost: Odpočítávání",
    "soundRollingVolume": "Svazek: Objekt",
    "objectName0": "Šestistranné kostky",
    "objectName1": "Mince",
    "objectName2": "Dřeváky",
    "objectName3": "šogi kousky",
    "objectName4": "Kámen, nůžky, papír",
    "language": "Jazyk",
    "usage1": "Kostky",
    "usage2": "Podporuje 1 až 6 kostek. Kostky se házejí poklepáním na obrazovku.",
    "usage3": "Dobu odpočítávání do hodu kostkou lze nastavit. Pokud odpočítávání není potřeba, nastavte na nulu.",
    "usage4": "Hlasitost zvukových efektů lze upravit. Pokud zvukový efekt nepotřebujete, nastavte jej na nulu.",

    "dummy": "dummy"
}

lib/l10n/app_da.arb

{
    "@@locale":"da",
    "@locale": {
        "description": "デンマーク"
    },
    "setting": "Indstilling",
    "start": "Start med at trykke på skærmen",
    "objectChoice": "Objekt",
    "objectCount": "Objektantal",
    "countdownTime": "Nedtælling",
    "soundThrowVolume": "Lydstyrke: Nedtælling",
    "soundRollingVolume": "Volumen: Objekt",
    "objectName0": "Sekssidede terninger",
    "objectName1": "Mønt",
    "objectName2": "Træsko",
    "objectName3": "shogi stykker",
    "objectName4": "Sten, saks, papir",
    "language": "Sprog",
    "usage1": "Terning",
    "usage2": "Den understøtter fra 1 til 6 terninger. Terninger kastes ved at trykke på skærmen.",
    "usage3": "Nedtællingstiden indtil terningerne kastes kan indstilles. Hvis der ikke er behov for nedtælling, skal du indstille til nul.",
    "usage4": "Lydstyrken for lydeffekter kan justeres. Hvis lydeffekten ikke er nødvendig, skal du indstille den til nul.",

    "dummy": "dummy"
}

lib/l10n/app_de.arb

{
    "@@locale":"de",
    "@locale": {
        "description": "ドイツ"
    },
    "setting": "Einstellung",
    "start": "Tippen Sie zunächst auf den Bildschirm",
    "objectChoice": "Objekt",
    "objectCount": "Objektanzahl",
    "countdownTime": "Countdown",
    "soundThrowVolume": "Lautstärke: Countdown",
    "soundRollingVolume": "Volumen: Objekt",
    "objectName0": "Sechsseitiger Würfel",
    "objectName1": "Münze",
    "objectName2": "Holzschuhe",
    "objectName3": "Shogi-Stücke",
    "objectName4": "Stein, Schere, Papier",
    "language": "Sprache",
    "usage1": "Würfel",
    "usage2": "Es unterstützt 1 bis 6 Würfel. Würfel werden durch Antippen des Bildschirms geworfen.",
    "usage3": "Die Countdown-Zeit bis zum Würfeln kann eingestellt werden. Wenn kein Countdown erforderlich ist, stellen Sie ihn auf Null ein.",
    "usage4": "Die Lautstärke der Soundeffekte kann angepasst werden. Wenn der Soundeffekt nicht benötigt wird, stellen Sie ihn auf Null.",

    "dummy": "dummy"
}

lib/l10n/app_el.arb

{
    "@@locale":"el",
    "@locale": {
        "description": "ギリシャ"
    },
    "setting": "Σύνθεση",
    "start": "Ξεκινήστε πατώντας στην οθόνη",
    "objectChoice": "Αντικείμενο",
    "objectCount": "Καταμέτρηση αντικειμένων",
    "countdownTime": "Αντίστροφη μέτρηση",
    "soundThrowVolume": "Όγκος: Αντίστροφη μέτρηση",
    "soundRollingVolume": "Όγκος: Αντικείμενο",
    "objectName0": "Ζάρια έξι όψεων",
    "objectName1": "Κέρμα",
    "objectName2": "Ξύλινα τσόκαρα",
    "objectName3": "κομμάτια σόγκι",
    "objectName4": "Πέτρα, ψαλίδι, χαρτί",
    "language": "Γλώσσα",
    "usage1": "Ζάρια",
    "usage2": "Υποστηρίζει από 1 έως 6 ζάρια. Τα ζάρια ρίχνονται πατώντας στην οθόνη.",
    "usage3": "Ο χρόνος αντίστροφης μέτρησης μέχρι να ρίξουν τα ζάρια μπορεί να ρυθμιστεί. Εάν δεν απαιτείται αντίστροφη μέτρηση, ορίστε το στο μηδέν.",
    "usage4": "Η ένταση των ηχητικών εφέ μπορεί να ρυθμιστεί. Εάν το ηχητικό εφέ δεν χρειάζεται, ρυθμίστε το στο μηδέν.",

    "dummy": "dummy"
}

lib/l10n/app_en.arb

{
    "@@locale":"en",
    "@locale": {
        "description": "英語"
    },
    "setting": "Setting",
    "start": "Start by tapping on the screen",
    "objectChoice": "Object",
    "objectCount": "Object count",
    "countdownTime": "Countdown",
    "soundThrowVolume": "Volume: Countdown",
    "soundRollingVolume": "Volume: Object",
    "objectName0": "Six sided dice",
    "objectName1": "Coin",
    "objectName2": "Wooden clogs",
    "objectName3": "Shogi piece",
    "objectName4": "Rock, Paper, Scissors",
    "language": "Language",
    "usage1": "Dice",
    "usage2": "It supports from 1 to 6 dice. Dice are rolled by tapping in the screen.",
    "usage3": "The countdown time until the dice are rolled can be set. If no countdown is needed, set to zero.",
    "usage4": "The volume of sound effects can be adjusted. If the sound effect is not needed, set it to zero.",

    "dummy": "dummy"
}

lib/l10n/app_es.arb

{
    "@@locale":"es",
    "@locale": {
        "description": "スペイン"
    },
    "setting": "Configuración",
    "start": "Comience tocando la pantalla",
    "objectChoice": "Objeto",
    "objectCount": "Recuento de objetos",
    "countdownTime": "cuenta regresiva",
    "soundThrowVolume": "Volumen: cuenta regresiva",
    "soundRollingVolume": "Volumen: Objeto",
    "objectName0": "Dados de seis caras",
    "objectName1": "Moneda",
    "objectName2": "Zuecos de madera",
    "objectName3": "piezas de shogi",
    "objectName4": "Piedra, papel o tijera",
    "language": "Idioma",
    "usage1": "Dado",
    "usage2": "Admite de 1 a 6 dados. Los dados se lanzan tocando en la pantalla.",
    "usage3": "Se puede configurar el tiempo de cuenta atrás hasta que se lancen los dados. Si no se necesita una cuenta regresiva, póngala en cero.",
    "usage4": "El volumen de los efectos de sonido se puede ajustar. Si el efecto de sonido no es necesario, configúrelo en cero.",

    "dummy": "dummy"
}

lib/l10n/app_et.arb

{
    "@@locale":"et",
    "@locale": {
        "description": "エストニア"
    },
    "setting": "Seadistamine",
    "start": "Alustage ekraani puudutamisest",
    "objectChoice": "Objekt",
    "objectCount": "Objektide arv",
    "countdownTime": "Pöördloendus",
    "soundThrowVolume": "Helitugevus: Countdown",
    "soundRollingVolume": "Maht: objekt",
    "objectName0": "Kuuepoolne täring",
    "objectName1": "Münt",
    "objectName2": "Puidust puukingad",
    "objectName3": "shogi tükid",
    "objectName4": "Kivi, käärid, paber",
    "language": "Keel",
    "usage1": "Täringud",
    "usage2": "See toetab 1 kuni 6 täringut. Täringut veeretatakse ekraanil puudutades.",
    "usage3": "Loendusaega kuni täringu veeretamiseni saab seadistada. Kui pöördloendust pole vaja, seadke null.",
    "usage4": "Heliefektide helitugevust saab reguleerida. Kui heliefekti pole vaja, seadke see nulli.",

    "dummy": "dummy"
}

lib/l10n/app_fi.arb

{
    "@@locale":"fi",
    "@locale": {
        "description": "フィンランド"
    },
    "setting": "Asetus",
    "start": "Aloita napauttamalla näyttöä",
    "objectChoice": "Esine",
    "objectCount": "Objektien määrä",
    "countdownTime": "Lähtölaskenta",
    "soundThrowVolume": "Äänenvoimakkuus: Lähtölaskenta",
    "soundRollingVolume": "Volyymi: Objekti",
    "objectName0": "Kuusipuolinen noppa",
    "objectName1": "Kolikko",
    "objectName2": "Puiset puukengät",
    "objectName3": "shogin palaset",
    "objectName4": "Kivi, sakset, paperi",
    "language": "Kieli",
    "usage1": "Dice",
    "usage2": "Se tukee 1-6 noppaa. Noppia heitetään napauttamalla näyttöä.",
    "usage3": "Ajastinaika noppaa heittämiseen voidaan asettaa. Jos lähtölaskentaa ei tarvita, aseta se nollaan.",
    "usage4": "Äänitehosteiden äänenvoimakkuutta voidaan säätää. Jos äänitehostetta ei tarvita, aseta se nollaan.",

    "dummy": "dummy"
}

lib/l10n/app_fr.arb

{
    "@@locale":"fr",
    "@locale": {
        "description": "フランス"
    },
    "setting": "Paramètre",
    "start": "Commencez par appuyer sur l'écran",
    "objectChoice": "Objet",
    "objectCount": "Nombre d'objets",
    "countdownTime": "Compte à rebours",
    "soundThrowVolume": "Volume: Compte à rebours",
    "soundRollingVolume": "Volume: Objet",
    "objectName0": "Dés à six faces",
    "objectName1": "Pièce de monnaie",
    "objectName2": "Sabots en bois",
    "objectName3": "morceaux de shogi",
    "objectName4": "Pierre, papier, ciseaux",
    "language": "Langue",
    "usage1": "Dé",
    "usage2": "Il supporte de 1 à 6 dés. Les dés sont lancés en appuyant sur l'écran.",
    "usage3": "Le temps de compte à rebours jusqu'à ce que les dés soient lancés peut être réglé. Si aucun compte à rebours n’est nécessaire, réglez-le sur zéro.",
    "usage4": "Le volume des effets sonores peut être ajusté. Si l'effet sonore n'est pas nécessaire, réglez-le sur zéro.",

    "dummy": "dummy"
}

lib/l10n/app_hu.arb

{
    "@@locale":"hu",
    "@locale": {
        "description": "ハンガリー"
    },
    "setting": "Beállítás",
    "start": "Kezdje a képernyő megérintésével",
    "objectChoice": "Tárgy",
    "objectCount": "Objektumszám",
    "countdownTime": "Visszaszámlálás",
    "soundThrowVolume": "Hangerő: Visszaszámlálás",
    "soundRollingVolume": "Kötet: tárgy",
    "objectName0": "Hat oldalú dobókocka",
    "objectName1": "Érme",
    "objectName2": "Fa klumpa",
    "objectName3": "shogi darabok",
    "objectName4": "Kő, papír, olló",
    "language": "Nyelv",
    "usage1": "Dobókocka",
    "usage2": "1-6 kockát támogat. A kocka a képernyő megérintésével dobható.",
    "usage3": "A visszaszámlálás ideje a kockadobásig beállítható. Ha nincs szükség visszaszámlálásra, állítsa nullára.",
    "usage4": "A hangeffektusok hangereje állítható. Ha nincs szükség hangeffektusra, állítsa nullára.",

    "dummy": "dummy"
}

lib/l10n/app_it.arb

{
    "@@locale":"it",
    "@locale": {
        "description": "イタリア"
    },
    "setting": "Collocamento",
    "start": "Inizia toccando lo schermo",
    "objectChoice": "Oggetto",
    "objectCount": "Conteggio oggetti",
    "countdownTime": "Conto alla rovescia",
    "soundThrowVolume": "Volume: Conto alla rovescia",
    "soundRollingVolume": "Volume: Oggetto",
    "objectName0": "Dadi a sei facce",
    "objectName1": "Moneta",
    "objectName2": "Zoccoli di legno",
    "objectName3": "pezzi di Shogi",
    "objectName4": "Carta, forbice, sasso",
    "language": "Lingua",
    "usage1": "Dado",
    "usage2": "Supporta da 1 a 6 dadi. I dadi vengono lanciati toccando lo schermo.",
    "usage3": "È possibile impostare il tempo del conto alla rovescia fino al lancio dei dadi. Se non è necessario il conto alla rovescia, impostarlo su zero.",
    "usage4": "È possibile regolare il volume degli effetti sonori. Se l'effetto sonoro non è necessario, impostalo su zero.",

    "dummy": "dummy"
}

lib/l10n/app_ja.arb

{
    "@@locale":"ja",
    "@locale": {
        "description": "日本"
    },
    "setting": "設定",
    "start": "画面内をタップでスタート",
    "objectChoice": "物体選択",
    "objectCount": "物体の数",
    "countdownTime": "カウントダウン",
    "soundThrowVolume": "音量:カウントダウン",
    "soundRollingVolume": "音量:物体",
    "objectName0": "6面体サイコロ",
    "objectName1": "コイン",
    "objectName2": "下駄",
    "objectName3": "将棋の駒",
    "objectName4": "じゃんけん",
    "language": "言語",
    "usage1": "サイコロです。",
    "usage2": "1個から6個までのサイコロに対応しています。画面内をタップでサイコロが振られます。",
    "usage3": "サイコロを振るまでのカウントダウン時間を設定可能です。カウントダウンが不要な場合はゼロにしてください。",
    "usage4": "効果音の音量を調整可能です。効果音が不要な場合はゼロにしてください。",

    "dummy": "dummy"
}

lib/l10n/app_lt.arb

{
    "@@locale":"lt",
    "@locale": {
        "description": "リトアニア"
    },
    "setting": "Nustatymas",
    "start": "Pradėkite bakstelėdami ekraną",
    "objectChoice": "Objektas",
    "objectCount": "Objektų skaičius",
    "countdownTime": "Atgalinis skaičiavimas",
    "soundThrowVolume": "Garsas: atgalinis skaičiavimas",
    "soundRollingVolume": "Apimtis: Objektas",
    "objectName0": "Šešių pusių kauliukai",
    "objectName1": "Moneta",
    "objectName2": "Medinės klumpės",
    "objectName3": "shogi gabaliukai",
    "objectName4": "Akmuo, popierius, žirklės",
    "language": "Kalba",
    "usage1": "Kauliukai",
    "usage2": "Jis palaiko nuo 1 iki 6 kauliukų. Kauliukai ridenami bakstelėjus ekraną.",
    "usage3": "Galima nustatyti atgalinės atskaitos laiką iki kauliukų metimo. Jei atgalinės atskaitos nereikia, nustatykite į nulį.",
    "usage4": "Galima reguliuoti garso efektų garsumą. Jei garso efektas nereikalingas, nustatykite jį į nulį.",

    "dummy": "dummy"
}

lib/l10n/app_lv.arb

{
    "@@locale":"lv",
    "@locale": {
        "description": "ラトビア"
    },
    "setting": "Iestatījums",
    "start": "Sāciet, pieskaroties ekrānam",
    "objectChoice": "Objekts",
    "objectCount": "Objektu skaits",
    "countdownTime": "Atpakaļskaitīšana",
    "soundThrowVolume": "Skaļums: Atpakaļskaitīšana",
    "soundRollingVolume": "Apjoms: Objekts",
    "objectName0": "Sešpusēji kauliņi",
    "objectName1": "Monēta",
    "objectName2": "Koka koka tupeles",
    "objectName3": "shogi gabali",
    "objectName4": "Akmenis, šķēres, papīrs",
    "language": "Valoda",
    "usage1": "Kauliņi",
    "usage2": "Tas atbalsta no 1 līdz 6 kauliņiem. Kauliņi tiek ripināti, pieskaroties ekrānam.",
    "usage3": "Atpakaļskaitīšanas laiku līdz kauliņu ripināšanai var iestatīt. Ja atpakaļskaitīšana nav nepieciešama, iestatiet uz nulli.",
    "usage4": "Skaņas efektu skaļumu var regulēt. Ja skaņas efekts nav nepieciešams, iestatiet to uz nulli.",

    "dummy": "dummy"
}

lib/l10n/app_nl.arb

{
    "@@locale":"nl",
    "@locale": {
        "description": "オランダ"
    },
    "setting": "Instelling",
    "start": "Begin door op het scherm te tikken",
    "objectChoice": "Voorwerp",
    "objectCount": "Aantal objecten",
    "countdownTime": "Aftellen",
    "soundThrowVolume": "Volume: aftellen",
    "soundRollingVolume": "Volume: Voorwerp",
    "objectName0": "Zeszijdige dobbelstenen",
    "objectName1": "Munt",
    "objectName2": "Houten klompen",
    "objectName3": "shogi-stukken",
    "objectName4": "Steen, papier, schaar",
    "language": "Taal",
    "usage1": "Dobbelsteen",
    "usage2": "Het ondersteunt 1 tot 6 dobbelstenen. Dobbelstenen worden gegooid door op het scherm te tikken.",
    "usage3": "De afteltijd totdat de dobbelstenen worden gegooid, kan worden ingesteld. Als er geen aftelling nodig is, stelt u deze in op nul.",
    "usage4": "Het volume van geluidseffecten kan worden aangepast. Als het geluidseffect niet nodig is, stelt u dit in op nul.",

    "dummy": "dummy"
}

lib/l10n/app_pl.arb

{
    "@@locale":"pl",
    "@locale": {
        "description": "ポーランド"
    },
    "setting": "Ustawienie",
    "start": "Zacznij od dotknięcia ekranu",
    "objectChoice": "Obiekt",
    "objectCount": "Liczba obiektów",
    "countdownTime": "Odliczanie",
    "soundThrowVolume": "Tom: Odliczanie",
    "soundRollingVolume": "Objętość: Obiekt",
    "objectName0": "Sześciościenna kostka",
    "objectName1": "Moneta",
    "objectName2": "Drewniane chodaki",
    "objectName3": "kawałki shogi",
    "objectName4": "Kamień, papier, nożyce",
    "language": "Język",
    "usage1": "Kostka do gry",
    "usage2": "Obsługuje od 1 do 6 kości. Kości są rzucane poprzez dotknięcie ekranu.",
    "usage3": "Można ustawić czas odliczania do momentu rzucenia kostką. Jeśli odliczanie nie jest potrzebne, ustaw je na zero.",
    "usage4": "Można regulować głośność efektów dźwiękowych. Jeśli efekt dźwiękowy nie jest potrzebny, ustaw go na zero.",

    "dummy": "dummy"
}

lib/l10n/app_pt.arb

{
    "@@locale":"pt",
    "@locale": {
        "description": "ポルトガル"
    },
    "setting": "Contexto",
    "start": "Comece tocando na tela",
    "objectChoice": "Objeto",
    "objectCount": "Contagem de objetos",
    "countdownTime": "Contagem regressiva",
    "soundThrowVolume": "Volume: contagem regressiva",
    "soundRollingVolume": "Volume: Objeto",
    "objectName0": "Dados de seis lados",
    "objectName1": "Moeda",
    "objectName2": "Tamancos de madeira",
    "objectName3": "peças de shogi",
    "objectName4": "Pedra, papel, tesoura",
    "language": "Linguagem",
    "usage1": "Dados",
    "usage2": "Suporta de 1 a 6 dados. Os dados são lançados tocando na tela.",
    "usage3": "O tempo de contagem regressiva até que os dados sejam lançados pode ser definido. Se nenhuma contagem regressiva for necessária, defina como zero.",
    "usage4": "O volume dos efeitos sonoros pode ser ajustado. Se o efeito sonoro não for necessário, defina-o como zero.",

    "dummy": "dummy"
}

lib/l10n/app_ro.arb

{
    "@@locale":"ro",
    "@locale": {
        "description": "ルーマニア"
    },
    "setting": "Setare",
    "start": "Începeți prin atingerea ecranului",
    "objectChoice": "Obiect",
    "objectCount": "Număr de obiecte",
    "countdownTime": "Numărătoare inversă",
    "soundThrowVolume": "Volum: numărătoare inversă",
    "soundRollingVolume": "Volum: obiect",
    "objectName0": "Zaruri cu șase fețe",
    "objectName1": "Monedă",
    "objectName2": "Saboți de lemn",
    "objectName3": "piese de shogi",
    "objectName4": "Piatră, hârtie, foarfece",
    "language": "Limba",
    "usage1": "Zaruri",
    "usage2": "Suportă de la 1 până la 6 zaruri. Zarurile sunt aruncate prin atingerea ecranului.",
    "usage3": "Timpul de numărătoare inversă până la aruncarea zarurilor poate fi stabilit. Dacă nu este necesară numărătoarea inversă, setați la zero.",
    "usage4": "Volumul efectelor sonore poate fi reglat. Dacă efectul sonor nu este necesar, setați-l la zero.",

    "dummy": "dummy"
}

lib/l10n/app_ru.arb

{
    "@@locale":"ru",
    "@locale": {
        "description": "ロシア"
    },
    "setting": "Параметр",
    "start": "Начните с нажатия на экран",
    "objectChoice": "Объект",
    "objectCount": "Количество объектов",
    "countdownTime": "Обратный отсчет",
    "soundThrowVolume": "Объем: Обратный отсчет",
    "soundRollingVolume": "Объем: Объект",
    "objectName0": "Шестигранный кубик",
    "objectName1": "Монета",
    "objectName2": "Деревянные сабо",
    "objectName3": "кусочки сёги",
    "objectName4": "Камень, ножницы, бумага",
    "language": "Язык",
    "usage1": "Игральная кость",
    "usage2": "Он поддерживает от 1 до 6 кубиков. Кости бросаются нажатием на экран.",
    "usage3": "Можно установить время обратного отсчета до броска кубиков. Если обратный отсчет не требуется, установите его на ноль.",
    "usage4": "Громкость звуковых эффектов можно регулировать. Если звуковой эффект не нужен, установите его на ноль.",

    "dummy": "dummy"
}

lib/l10n/app_sk.arb

{
    "@@locale":"sk",
    "@locale": {
        "description": "スロバキア"
    },
    "setting": "Nastavenie",
    "start": "Začnite ťuknutím na obrazovku",
    "objectChoice": "Objekt",
    "objectCount": "Počet objektov",
    "countdownTime": "Odpočítavanie",
    "soundThrowVolume": "Hlasitosť: Odpočítavanie",
    "soundRollingVolume": "Objem: Objekt",
    "objectName0": "Šesťstranné kocky",
    "objectName1": "mince",
    "objectName2": "Drevené dreváky",
    "objectName3": "šogi kúsky",
    "objectName4": "Kameň, papier, nožnice",
    "language": "Jazyk",
    "usage1": "Kocky",
    "usage2": "Podporuje 1 až 6 kociek. Kocky sa hádžu poklepaním na obrazovku.",
    "usage3": "Dá sa nastaviť čas odpočítavania do hodu kockou. Ak nie je potrebné žiadne odpočítavanie, nastavte ho na nulu.",
    "usage4": "Hlasitosť zvukových efektov je možné nastaviť. Ak zvukový efekt nie je potrebný, nastavte ho na nulu.",

    "dummy": "dummy"
}

lib/l10n/app_sv.arb

{
    "@@locale":"sv",
    "@locale": {
        "description": "スウェーデン"
    },
    "setting": "Miljö",
    "start": "Börja med att trycka på skärmen",
    "objectChoice": "Objekt",
    "objectCount": "Antal objekt",
    "countdownTime": "Nedräkning",
    "soundThrowVolume": "Volym: Nedräkning",
    "soundRollingVolume": "Volym: Objekt",
    "objectName0": "Sexsidiga tärningar",
    "objectName1": "Mynt",
    "objectName2": "Träskor",
    "objectName3": "shogi bitar",
    "objectName4": "Sten, sax, påse",
    "language": "Språk",
    "usage1": "Tärningar",
    "usage2": "Den stöder från 1 till 6 tärningar. Tärningar rullas genom att trycka på skärmen.",
    "usage3": "Nedräkningstiden tills tärningarna kastas kan ställas in. Om ingen nedräkning behövs, ställ in på noll.",
    "usage4": "Volymen på ljudeffekter kan justeras. Om ljudeffekten inte behövs, ställ in den på noll.",

    "dummy": "dummy"
}

lib/l10n/app_th.arb

{
    "@@locale":"th",
    "@locale": {
        "description": "タイ"
    },
    "setting": "การตั้งค่า",
    "start": "เริ่มต้นด้วยการแตะบนหน้าจอ",
    "objectChoice": "วัตถุ",
    "objectCount": "จำนวนวัตถุ",
    "countdownTime": "นับถอยหลัง",
    "soundThrowVolume": "ปริมาณ: นับถอยหลัง",
    "soundRollingVolume": "เล่ม: วัตถุ",
    "objectName0": "ลูกเต๋าหกด้าน",
    "objectName1": "เหรียญ",
    "objectName2": "ไม้อุดตัน",
    "objectName3": "ชิ้นโชกิ",
    "objectName4": "ค้อน, กรรไกร, กระดาษ",
    "language": "ภาษา",
    "usage1": "ลูกเต๋า",
    "usage2": "รองรับตั้งแต่ 1 ถึง 6 ลูกเต๋า ลูกเต๋าจะถูกทอยโดยการแตะในหน้าจอ",
    "usage3": "สามารถตั้งเวลานับถอยหลังจนกระทั่งทอยลูกเต๋าได้ หากไม่จำเป็นต้องนับถอยหลัง ให้ตั้งค่าเป็นศูนย์",
    "usage4": "สามารถปรับระดับเสียงเอฟเฟกต์ได้ หากไม่ต้องการเอฟเฟกต์เสียง ให้ตั้งค่าเป็นศูนย์",

    "dummy": "dummy"
}

lib/l10n/app_zh.arb

{
    "@@locale":"zh",
    "@locale": {
        "description": "中国"
    },
    "setting": "环境",
    "start": "首先点击屏幕开始",
    "objectChoice": "目的",
    "objectCount": "对象计数",
    "countdownTime": "倒数",
    "soundThrowVolume": "音量:倒计时",
    "soundRollingVolume": "体积:物体",
    "objectName0": "六面骰子",
    "objectName1": "硬币",
    "objectName2": "木鞋",
    "objectName3": "将棋棋子",
    "objectName4": "石头, 剪刀, 布",
    "language": "语言",
    "usage1": "骰子",
    "usage2": "它支持 1 到 6 个骰子。 通过点击屏幕来掷骰子。",
    "usage3": "可以设置掷骰子之前的倒计时时间。 如果不需要倒计时,则设置为零。",
    "usage4": "音效的音量可以调节。 如果不需要音效,则设置为零。",

    "dummy": "dummy"
}