ソースコード source code

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

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

下記コードの最終ビルド日: 2023-10-09

pubspec.yaml

name: lotteryslot
description: "Lottery Slot"
# 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: 1.0.1+2

environment:
  sdk: '>=3.2.0-87.0.dev <4.0.0'

# 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.5
  package_info_plus: ^4.1.0
  shared_preferences: ^2.0.17
  http: ^1.1.0
  flutter_localizations:    #多言語ライブラリの本体    # .arbファイルを更新したら flutter gen-l10n
    sdk: flutter
  intl: ^0.18.1     #多言語やフォーマッタなどの関連ライブラリ
  flutter_tts: ^3.8.2
  google_mobile_ads: ^2.4.0
  just_audio: ^0.9.35

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_launcher_icons: ^0.13.1    #flutter pub run flutter_launcher_icons
  flutter_native_splash: ^2.3.2     #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: ^2.0.1

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

flutter_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: '#2f050d'
  image: 'assets/image/splash.png'
  color_dark: '#2f050d'
  image_dark: 'assets/image/splash.png'
  fullscreen: true
  android_12:
    icon_background_color: '#2f050d'
    image: 'assets/image/splash.png'
    icon_background_color_dark: '#2f050d'
    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/assets-and-images/#resolution-aware

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # 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/custom-fonts/#from-packages

lib/main.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-09
///

import 'dart:math';
import 'dart:async';
import 'dart:io';
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';    //##################### Webの時はコメントアウトする。Android,iOSの時は有効にする
import 'package:flutter/foundation.dart' show kIsWeb;

//自身で作成したclassを読み込む
import 'package:lotteryslot/const_value.dart';
import 'package:lotteryslot/language_state.dart';
import 'package:lotteryslot/version_state.dart';
import 'package:lotteryslot/game.dart';
import 'package:lotteryslot/tts_state.dart';
import 'package:lotteryslot/text_to_speech.dart';
import 'package:lotteryslot/setting.dart';
import 'package:lotteryslot/ad_mob.dart';
import 'package:lotteryslot/audio_play.dart';
import 'package:lotteryslot/page_state.dart';

void main() {
  //MobileAds.instance.initialize(); ここに入れると進行しなかった
  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) {
    MobileAds.instance.initialize();    //##################### Webの時はコメントアウトする。Android,iOSの時は有効にする
    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> {
  final AdMob _adMob = AdMob(); //広告表示
  final AudioPlay _sound = AudioPlay(); //音再生用
  final TextToSpeech _textToSpeech = TextToSpeech();  //読み上げ用
  final Game _game = Game();  //ゲームのデータを一時的に保持するなどの役目
  bool _busyFlag = false; //抽選機動作中はtrue
  final List<double> _digitPositions = [-1.0,-0.82,-0.64,-0.46,-0.28,-0.1,0.08,0.26,0.44,0.61,0.81,0.99]; //0,1,2,3,4,5,6,7,8,9,0,end
  final List<double> _digitAlignment = [-1.0,-1.0,-1.0,-1.0,-1.0];  //これが数字の位置
  final List<double> _digitSpeeds = [0.010,0.011,0.012,0.013,0.014];  //5個の各数字の切り替わり速度。シャッフルされ使用される
  final double _digitSpeedMax = 0.015;  //_digitSpeedsの最大値よりも少しだけ大きくしておく
  final TextEditingController _controllerDisplayPrizeString = TextEditingController();  //賞の表示
  final TextEditingController _controllerDisplayRemainString = TextEditingController();  //残数の表示
  final TextEditingController _controllerDisplayHistoryString = TextEditingController();  //履歴の表示

  //アプリのバージョン取得
  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();
  }
  //ページ終了時に一度だけ呼ばれる
  @override
  void dispose() {
    PageState.setCurrentPage('');
    _adMob.dispose();
    super.dispose();
  }
  //数字5桁の表示 columnは[0,1,2,3,4]
  Widget _digit(dynamic constraints, double left, int column) {
    return Positioned(
      left: constraints.maxHeight * left,
      top: constraints.maxHeight * 0.314,
      child: ClipRect(
        child: Align(
          alignment: Alignment(0, _digitAlignment[column]),
          widthFactor: 1.0, // 中央の50%の幅
          heightFactor: 0.080, // 中央の50%の高さ
          child: SizedBox(
            width: constraints.maxHeight * 0.08,
            child: Image.asset(ConstValue.imageReel),
          )
        )
      )
    );
  }
  //抽選機の正方形エリア
  Widget _stage() {
    return AspectRatio(
      aspectRatio: 1 / 1,
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          return Stack(
            children: <Widget>[
              Image.asset(ConstValue.machineImages[_game.machineImageIndex]),  //背景画像
              _digit(constraints, 0.179, 4),  //5桁目の数字
              _digit(constraints, 0.301, 3),  //4桁目の数字
              _digit(constraints, 0.431, 2),  //3桁目の数字
              _digit(constraints, 0.558, 1),  //2桁目の数字
              _digit(constraints, 0.681, 0),  //1桁目の数字
              Image.asset(ConstValue.imageMachineOver),  //背景画像
              _prizeArea(), //賞は重ねて表示
            ]
          );
        }
      )
    );
  }
  //スタートボタン
  Widget _startButton() {
    return GestureDetector(
      onTap: () {
        _lottery(); //抽選開始
      },
      child: Opacity(
        opacity: _busyFlag ? 0.3 : 1, //抽選中は薄くする
        child: Container(
          width: double.infinity,
          padding: const EdgeInsets.all(12.0),
          color: ConstValue.colorButtonBack,
          child: Center(
            child: Text(AppLocalizations.of(context)!.start,
              style: const TextStyle(
                color: ConstValue.colorButtonFore,
                fontSize: 24.0,
              )
            )
          )
        )
      )
    );
  }
  //画面全体
  @override
  Widget build(BuildContext context) {
    //このページが開いたときに一度だけ実行される処理を記述。initStateではタイミングが合わない為。
    if (PageState.getCurrentPage() != 'main') {
      PageState.setCurrentPage('main');
      _sound.machineSoundVolume = _game.machineSoundVolume;
      _sound.prizeSoundVolume = _game.prizeSoundVolume;
      _textToSpeech.setVolume(_game.speakSoundVolume);
      _sound.playMachineStop(); //一度だけ鳴らす
      (() async {
        final int speakVoiceIndex = await TtsState.getSpeakVoiceIndex();
        await _textToSpeech.setSpeakVoiceFromIndex(speakVoiceIndex);
      })();
      if (!kIsWeb && Platform.isAndroid) {
        //Androidの場合、一発目の音が出ないのでここで鳴らしておく。
        _sound.playMachineStop();
      } else if (!kIsWeb && Platform.isIOS) {
        //何もしない
      }
    }
    return Scaffold(
      appBar: AppBar(
        backgroundColor: ConstValue.colorBack,
        //タイトル表示
        title: const Text('Lottery Reel',
          style: TextStyle(
            color: ConstValue.colorButtonFore,
            fontSize: 15.0,
          )
        ),
        //設定ボタン
        actions: <Widget>[
          TextButton(
            onPressed: () async {
              if (_busyFlag) {
                return;
              }
              bool? ret = await Navigator.of(context).push(
                MaterialPageRoute<bool>(
                  builder: (context) => SettingPage(game: _game),
                ),
              );
              //awaitで呼び出しているので、settingから戻ったら以下が実行される。
              if (ret!) { //設定で適用だった場合
                _getCurrentLocale();
                _sound.machineSoundVolume = _game.machineSoundVolume;
                _sound.prizeSoundVolume = _game.prizeSoundVolume;
                _textToSpeech.setVolume(_game.speakSoundVolume);
                List<int> historyNumbers = _game.historyNumbers;
                historyNumbers = historyNumbers.reversed.toList();
                _controllerDisplayHistoryString.text = historyNumbers.join(', ');
                setState(() {});
              }
            },
            child: Opacity(
              opacity: _busyFlag ? 0.3 : 1,
              child: Text(
                AppLocalizations.of(context)!.setting,
                style: const TextStyle(
                  color: ConstValue.colorButtonFore,
                )
              )
            )
          )
        ]
      ),
      body: SafeArea(
        child: Container(
          color: ConstValue.colorBack,
          child: Column(children:[
            Expanded(
              child: SingleChildScrollView(
                child: Column(
                  children: <Widget>[
                    _stage(), //抽選機
                    _startButton(), //STARTボタン
                    _remainArea(),  //残数表示
                    _historyArea(), //履歴表示
                  ]
                )
              )
            ),
            //広告
            Padding(
              padding: const EdgeInsets.only(top: 10, left: 0, right: 0, bottom: 0),
              child: SizedBox(
                width: double.infinity,
                child: _adMob.getAdBannerWidget(),
              )
            )
          ])
        )
      )
    );
  }
  //賞を表示
  Widget _prizeArea() {
    if (_controllerDisplayPrizeString.text == '') {
      //賞がない場合
      return Container();
    } else {
      //賞がある場合
      return TextField(
        controller: _controllerDisplayPrizeString,
        maxLines: null, //nullで複数行のテキストエリア
        readOnly: true,
        textAlign: TextAlign.center,
        style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
        decoration: const InputDecoration(
          filled: true,
          fillColor: Colors.yellowAccent,
          contentPadding: EdgeInsets.all(10.0),
          border: InputBorder.none,
        )
      );
    }
  }
  //残数を表示
  Widget _remainArea() {
    if (_game.historyDrawFlag == false) {
      //非表示設定の場合
      return Container();
    } else {
      //表示設定の場合
      return TextField(
        controller: _controllerDisplayRemainString,
        maxLines: null, //nullで複数行のテキストエリア
        readOnly: true,
        style: const TextStyle(color: Colors.orange),
        decoration: const InputDecoration(
          contentPadding: EdgeInsets.only(top: 0, left: 10, right: 10, bottom: 0),
          border: InputBorder.none,
        )
      );
    }
  }
  //抽選結果を表示
  Widget _historyArea() {
    if (_game.historyDrawFlag == false) {
      //非表示設定の場合
      return Container();
    } else {
      //表示設定の場合
      return TextField(
        controller: _controllerDisplayHistoryString,
        maxLines: null, //nullで複数行のテキストエリア
        readOnly: true,
        style: const TextStyle(color: Colors.white, fontSize: 26),
        decoration: const InputDecoration(
          contentPadding: EdgeInsets.only(top: 0, left: 10, right: 10, bottom: 10),
          border: InputBorder.none,
        )
      );
    }
  }
  //抽選
  void _lottery() async {
    if (_busyFlag) {
      return;
    }
    setState(() {
      _busyFlag = true;
    });
    _controllerDisplayPrizeString.text = '';  //賞を消す
    //履歴を表示
    (() async {
      final List<int> historyNumbers = _game.historyNumbers.reversed.toList();
      _controllerDisplayHistoryString.text = historyNumbers.join(', ');
    })();
    //抽選結果
    int nextNumber = await _nextNumber();
    if (nextNumber == -1) {
      //抽選がすべて終了
      setState(() {
        _busyFlag = false;
      });
      return;
    }
    if (await _game.addHistoryText(nextNumber) == false) {
      return; //2重登録の場合は何もしない
    }
    _digitSpeeds.shuffle(); //数字の動く速度を変更
    _sound.playMachineStart();
    //数字が動く全体の速度
    final int timeRemain = (10 - _game.machineSpeed) * 40;
    //数字を動かす処理。抽選番号、速度、次に止める桁[4]は5桁目
    //再帰で呼び出して4,3,2,1,0と順に数字を止めていく
    _lotteryRecursion(nextNumber, timeRemain, 4);
  }
  //再帰で数字を止めていく
  void _lotteryRecursion(int nextNumber, int timeRemain, int stopColumn) {
    for (int column = stopColumn; column >= 0; column--) {
      setState(() {
        //数字を動かす
        _digitAlignment[column] += _digitSpeeds[column];
        if (_digitAlignment[column] >= _digitPositions[10]) {
          _digitAlignment[column] = _digitPositions[0];
        }
      });
    }
    //残り時間を減らす
    timeRemain -= 1;
    if (timeRemain <= 0) {
      //残り時間が無くなったら順に桁を止めていく
      final double position = _digitPositions[(nextNumber / pow(10,stopColumn)).floor() % 10];
      if ((_digitAlignment[stopColumn] - position).abs() < _digitSpeedMax) {
        //各桁が止める位置になったら止めて、次の桁へ移る
        _digitAlignment[stopColumn] += _digitSpeedMax;  //少しずらして位置を合わせる
        _sound.playMachineStop();
        stopColumn -= 1;  //次の桁にする
      }
    }
    if (stopColumn >= 0) {
      //全ての桁が停止していないので再帰
      Timer(const Duration(milliseconds: 10), () =>
        _lotteryRecursion(nextNumber, timeRemain, stopColumn)
      );
    } else {
      //全ての桁が停止
      setState(() async {
        //音声が重なるのでずらす
        await Future.delayed(const Duration(milliseconds: 500));
        _textToSpeech.speak(nextNumber.toString());
        //賞を表示
        _prizeDraw(nextNumber);
        //履歴を表示
        setState(() {
          _controllerDisplayHistoryString.text = '* $nextNumber *, ${_controllerDisplayHistoryString.text}';
          _busyFlag = false;
        });
      });
    }
  }
  //抽選の次の数字を返す
  Future<int> _nextNumber() async {
    List<int> remains = _game.candidateNumbers;
    if (_game.historyNumbers.isNotEmpty) {
      //remainsからhistoryNumbersに含まれる数字を取り除く
      remains = remains.where((int num) => !_game.historyNumbers.contains(num)).toList();
    }
    if (remains.isEmpty) {
      return -1;  //残りが無くなった
    }
    //残数表示
    _controllerDisplayRemainString.text = 'Candidates:${_game.candidateNumbers.length} Results:${_game.historyNumbers.length + 1} Remaining:${remains.length - 1}';
    final int nextNumber = remains[Random().nextInt(remains.length)]; //1個取り出す
    return nextNumber;
  }
  //賞を表示
  void _prizeDraw(int nextNumber) async {
    for (Map<String,dynamic> mapListOne in _game.prizeList) {
      for (int j = 0; j < mapListOne['numbers'].length; j++) {
        if (mapListOne['numbers'][j] == nextNumber) {
          //賞に該当する場合は賞を表示
          _controllerDisplayPrizeString.text = mapListOne['prize'];
          await Future.delayed(const Duration(milliseconds: 1200));
          _sound.playPrize();
          return;
        }
      }
    }
    //賞に該当しない場合は賞を消去(既に消去されているはずなので無くても良い)
    _controllerDisplayPrizeString.text = '';
  }
}

lib/setting.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-05
///

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

import 'package:lotteryslot/const_value.dart';
import 'package:lotteryslot/language_state.dart';
import 'package:lotteryslot/version_state.dart';
import 'package:lotteryslot/tts_state.dart';
import 'package:lotteryslot/text_to_speech.dart';
import 'package:lotteryslot/game.dart';
import 'package:lotteryslot/ad_mob.dart';
import 'package:lotteryslot/page_state.dart';

class SettingPage extends StatefulWidget {
  //メインページでは SettingPage(game: _game) と渡している。
  //受け取った game は widget.game でアクセスできる。
  final Game game;
  const SettingPage({Key? key, required this.game}) : super(key: key);

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

class _SettingPageState extends State<SettingPage> {
  //これら変数はUIへの表示や入力の為に一時的に使用される。
  //適用ボタンによりGameクラスを介してPreferencesクラスでデバイスに値が保存される
  String _languageKey = ''; //言語コード 'en'
  String _languageValue = '';
  bool _candidateInitialFlag = false;
  bool _prizeInitialFlag = false;
  bool _historyInitialFlag = false;
  final TextEditingController _controllerCandidateText = TextEditingController();
  final TextEditingController _controllerPrizeText = TextEditingController();
  final TextEditingController _controllerHistoryText = TextEditingController();
  bool _historyDrawFlag = true;
  int _machineImageIndex = 0;
  int _machineSpeedValue = 1;
  double _machineSoundVolume = 1.0;
  double _prizeSoundVolume = 1.0;
  double _speakSoundVolume = 1.0;
  int _speakVoiceIndex = 0;
  List<DropdownMenuItem> _speakVoiceMenuItem = [];

  final AdMob _adMob = AdMob(); //広告

  //ページ起動時に一度だけ実行される
  @override
  void initState() {
    super.initState();
    _adMob.load();
  }
  //デバイスの読み上げ音声情報をドロップダウンリストにする
  Future<void> _initTts() async {
    TextToSpeech textToSpeech = TextToSpeech();
    List<String> speakVoices = await textToSpeech.getSpeakVoices();
    _speakVoiceMenuItem = [];
    for (int i = 0; i < speakVoices.length; i++) {
      _speakVoiceMenuItem.add(DropdownMenuItem(value: i, child: Text(speakVoices[i])));
    }
  }
  //ページ終了時に一度だけ実行される
  @override
  void dispose() {
    PageState.setCurrentPage('');
    _adMob.dispose();
    super.dispose();
  }
  //ページ描画
  @override
  Widget build(BuildContext context) {
    //このページが開いたときに一度だけ実行される処理を記述。initStateではタイミングが合わない為。
    if (PageState.getCurrentPage() != 'setting') {
      PageState.setCurrentPage('setting');
      _controllerCandidateText.text = widget.game.candidateText;
      _controllerPrizeText.text = widget.game.prizeText;
      _controllerHistoryText.text = widget.game.historyText;
      _historyDrawFlag = widget.game.historyDrawFlag;
      _machineImageIndex = widget.game.machineImageIndex;
      _machineSpeedValue = widget.game.machineSpeed;
      _machineSoundVolume = widget.game.machineSoundVolume;
      _prizeSoundVolume = widget.game.prizeSoundVolume;
      _speakSoundVolume = widget.game.speakSoundVolume;
      setState((){});
      (() async {
        _languageKey = await LanguageState.getLanguageCode();
        _languageValue = ConstValue.languageCode[_languageKey] ?? '';
        _speakVoiceIndex = await TtsState.getSpeakVoiceIndex();
        setState((){});
        await _initTts(); //エミュレータでは機能しないので最後に記述
      })();
    }
    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.colorSettingAccent,
        actions: [
          //設定OKボタン
          IconButton(
            icon: const Icon(Icons.check),
            onPressed: () async {
              await LanguageState.setLanguageCode(_languageKey);
              await TtsState.setSpeakVoiceIndex(_speakVoiceIndex);
              if (_candidateInitialFlag) {
                widget.game.setCandidateTextDefault();
              } else {
                widget.game.candidateText = _controllerCandidateText.text;
              }
              if (_prizeInitialFlag) {
                widget.game.setPrizeTextDefault();
              } else {
                widget.game.prizeText = _controllerPrizeText.text;
              }
              if (_historyInitialFlag) {
                widget.game.setHistoryTextDefault();
              } else {
                widget.game.historyText = _controllerHistoryText.text;
              }
              widget.game.historyDrawFlag = _historyDrawFlag;
              widget.game.machineImageIndex = _machineImageIndex;
              widget.game.machineSpeed = _machineSpeedValue;
              widget.game.machineSoundVolume = _machineSoundVolume;
              widget.game.prizeSoundVolume = _prizeSoundVolume;
              widget.game.speakSoundVolume = _speakSoundVolume;
              if (!mounted) {
                return;
              }
              Navigator.of(context).pop(true);  //trueを返す
            },
          ),
        ],
      ),
      body: 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: 0, left: 16, right: 16, bottom: 0),
                      child: Row(children:<Widget>[
                        Expanded(
                          child: Text(AppLocalizations.of(context)!.candidate,style: const TextStyle(fontSize: 16)),
                        ),
                        Text(AppLocalizations.of(context)!.initial),
                        Switch(
                          value: _candidateInitialFlag,
                          onChanged: (bool value) {
                            setState(() {
                              _candidateInitialFlag = value;
                            });
                          },
                        ),
                      ]),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 1, left: 16, right: 16, bottom: 16),
                      child: TextField(
                        controller: _controllerCandidateText,
                        maxLines: null, //nullで複数行のテキストエリア
                        decoration: const InputDecoration(
                          border: OutlineInputBorder(),
                        ),
                      ),
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 0),
                      child: Row(children:<Widget>[
                        Expanded(
                          child: Text(AppLocalizations.of(context)!.prize,style: const TextStyle(fontSize: 16)),
                        ),
                        Text(AppLocalizations.of(context)!.initial),
                        Switch(
                          value: _prizeInitialFlag,
                          onChanged: (bool value) {
                            setState(() {
                              _prizeInitialFlag = value;
                            });
                          },
                        ),
                      ]),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 1, left: 16, right: 16, bottom: 16),
                      child: TextField(
                        controller: _controllerPrizeText,
                        maxLines: null, //nullで複数行のテキストエリア
                        decoration: const InputDecoration(
                          border: OutlineInputBorder(),
                        ),
                      ),
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 0),
                      child: Row(children:<Widget>[
                        Expanded(
                          child: Text(AppLocalizations.of(context)!.history,style: const TextStyle(fontSize: 16)),
                        ),
                        Text(AppLocalizations.of(context)!.erase),
                        Switch(
                          value: _historyInitialFlag,
                          onChanged: (bool value) {
                            setState(() {
                              _historyInitialFlag = value;
                            });
                          },
                        ),
                      ]),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 1, left: 16, right: 16, bottom: 0),
                      child: TextField(
                        controller: _controllerHistoryText,
                        maxLines: null, //nullで複数行のテキストエリア
                        decoration: const InputDecoration(
                          border: OutlineInputBorder(),
                        ),
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 6),
                      child: Row(children:<Widget>[
                        Expanded(
                          child: Text(AppLocalizations.of(context)!.historyMainDraw,style: const TextStyle(fontSize: 16)),
                        ),
                        Switch(
                          value: _historyDrawFlag,
                          onChanged: (bool value) {
                            setState(() {
                              _historyDrawFlag = value;
                            });
                          },
                        ),
                      ]),
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.machineSpeed,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ]),
                    ),
                    Row(
                      children: <Widget>[
                        Padding(
                            padding: const EdgeInsets.only(top: 0, left: 16, right: 0, bottom: 0),
                            child: Text(_machineSpeedValue.toString())
                        ),
                        Expanded(
                          child: Slider(
                            value: _machineSpeedValue.toDouble(),
                            min: 1,
                            max: 9,
                            divisions: 9,
                            onChanged: (double value) {
                              setState(() {
                                _machineSpeedValue = value.toInt();
                              });
                            },
                          ),
                        ),
                      ],
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.machineSoundVolume,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ]),
                    ),
                    Row(
                      children: <Widget>[
                        Padding(
                            padding: const EdgeInsets.only(top: 0, left: 16, right: 0, bottom: 0),
                            child: Text(_machineSoundVolume.toString())
                        ),
                        Expanded(
                          child: Slider(
                            value: _machineSoundVolume,
                            min: 0.0,
                            max: 1.0,
                            divisions: 10,
                            onChanged: (double value) {
                              setState(() {
                                _machineSoundVolume = value;
                              });
                            },
                          ),
                        ),
                      ],
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.prizeSoundVolume,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ]),
                    ),
                    Row(
                      children: <Widget>[
                        Padding(
                            padding: const EdgeInsets.only(top: 0, left: 16, right: 0, bottom: 0),
                            child: Text(_prizeSoundVolume.toString())
                        ),
                        Expanded(
                          child: Slider(
                            value: _prizeSoundVolume,
                            min: 0.0,
                            max: 1.0,
                            divisions: 10,
                            onChanged: (double value) {
                              setState(() {
                                _prizeSoundVolume = value;
                              });
                            },
                          ),
                        ),
                      ],
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.speakSoundVolume,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ]),
                    ),
                    Row(
                      children: <Widget>[
                        Padding(
                            padding: const EdgeInsets.only(top: 0, left: 16, right: 0, bottom: 0),
                            child: Text(_speakSoundVolume.toString())
                        ),
                        Expanded(
                          child: Slider(
                            value: _speakSoundVolume,
                            min: 0.0,
                            max: 1.0,
                            divisions: 10,
                            onChanged: (double value) {
                              setState(() {
                                _speakSoundVolume = value;
                              });
                            },
                          ),
                        ),
                      ],
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.speakResult),
                        const Spacer(),
                      ]),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 4, left: 16, right: 16, bottom: 24),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.voice,
                          style: const TextStyle(
                            fontSize: 16,
                          ),
                        ),
                        const Spacer(),
                        DropdownButton(
                          items: _speakVoiceMenuItem,
                          onChanged: (value) {
                            setState(() {
                              _speakVoiceIndex = value;
                            });
                          },
                          value: _speakVoiceIndex,
                        ),
                      ]),
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 0),
                      child: Row(children: [
                        Text(AppLocalizations.of(context)!.machineImageIndex,style: const TextStyle(fontSize: 16)),
                        const Spacer(),
                      ]),
                    ),
                    Row(
                      children: <Widget>[
                        Padding(
                            padding: const EdgeInsets.only(top: 0, left: 16, right: 0, bottom: 0),
                            child: Text(_machineImageIndex.toString())
                        ),
                        Expanded(
                          child: Slider(
                            value: _machineImageIndex.toDouble(),
                            min: 0,
                            max: ConstValue.machineImages.length - 1,
                            divisions: ConstValue.machineImages.length - 1,
                            onChanged: (double value) {
                              setState(() {
                                _machineImageIndex = value.toInt();
                              });
                            },
                          ),
                        ),
                      ],
                    ),
                    _border(),
                    Padding(
                      padding: const EdgeInsets.only(top: 24, 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,
          ),
        ),
      ),
    );
  }
  //言語一覧表示
  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);
          });
        },
      ),
    );
  }

}

lib/game.dart

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

import 'package:lotteryslot/const_value.dart';
import 'package:lotteryslot/preferences.dart';

class Game {

  //以下の変数は状態が変化する毎に更新すること。
  bool _historyDrawFlag = true;
  int _machineImageIndex = 0;
  int _machineSpeed = 1;
  double _machineSoundVolume = 1.0;
  double _prizeSoundVolume = 1.0;
  double _speakSoundVolume = 1.0;
  String _candidateText = ConstValue.candidateTextDefault;
  List<int> _candidateNumbers = [];
  String _prizeText = ConstValue.prizeTextDefault;
  List<Map<String,dynamic>> _prizeList = [];
  String _historyText = ConstValue.historyTextDefault;
  List<int> _historyNumbers = [];

  //constructor
  Game() {
    _initialize();
  }
  //データを全て更新する
  Future<void> _initialize() async {
    _historyDrawFlag = await _getHistoryDrawFlag();
    _machineImageIndex = await _getMachineImageIndex();
    _machineSpeed = await _getMachineSpeed();
    _machineSoundVolume = await _getMachineSoundVolume();
    _prizeSoundVolume = await _getPrizeSoundVolume();
    _speakSoundVolume = await _getSpeakSoundVolume();
    _candidateText = await _getCandidateText();
    _candidateNumbers = await _getCandidateNumbers();
    _prizeText = await _getPrizeText();
    _prizeList = await _getPrizeList();
    _historyText = await _getHistoryText();
    _historyNumbers = await _getHistoryNumbers();
  }

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

  //getter/setter
  int get machineImageIndex {
    return _machineImageIndex;
  }
  set machineImageIndex(int num) {
    _setMachineImageIndex(num);
  }
  //背景画像インデックスを記録
  Future<void> _setMachineImageIndex(int num) async {
    _machineImageIndex = num;
    await Preferences.setMachineImageIndex(num);
  }
  //背景画像インデックスを返す
  Future<int> _getMachineImageIndex() async {
    final int num = await Preferences.getMachineImageIndex() ?? 0;
    return num;
  }

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

  //getter/setter
  bool get historyDrawFlag {
    return _historyDrawFlag;
  }
  set historyDrawFlag(bool flag) {
    _setHistoryDrawFlag(flag);
  }
  //抽選結果をメインページに表示を記録
  Future<void> _setHistoryDrawFlag(bool flag) async {
    _historyDrawFlag = flag;
    await Preferences.setHistoryDrawFlag(flag);
  }
  //抽選結果をメインページに表示を返す
  Future<bool> _getHistoryDrawFlag() async {
    final bool flag = await Preferences.getHistoryDrawFlag() ?? true;
    return flag;
  }

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

  //getter/setter
  int get machineSpeed {
    return _machineSpeed;
  }
  set machineSpeed(int num) {
    _setMachineSpeed(num);
  }
  //抽選機スピードを記録
  Future<void> _setMachineSpeed(int num) async {
    _machineSpeed = num;
    await Preferences.setMachineSpeed(num);
  }
  //抽選機スピードを返す
  Future<int> _getMachineSpeed() async {
    final int num = await Preferences.getMachineSpeed() ?? 1;
    return num;
  }

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

  //getter/setter
  double get machineSoundVolume {
    return _machineSoundVolume;
  }
  set machineSoundVolume(double num) {
    _setMachineSoundVolume(num);
  }
  //抽選機音量を記録
  Future<void> _setMachineSoundVolume(double num) async {
    _machineSoundVolume = num;
    await Preferences.setMachineSoundVolume(num);
  }
  //抽選機音量を返す
  Future<double> _getMachineSoundVolume() async {
    final double num = await Preferences.getMachineSoundVolume() ?? 1.0;
    return num;
  }

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

  //getter/setter
  double get prizeSoundVolume {
    return _prizeSoundVolume;
  }
  set prizeSoundVolume(double num) {
    _setPrizeSoundVolume(num);
  }
  //当選ベル音量を記録
  Future<void> _setPrizeSoundVolume(double num) async {
    _prizeSoundVolume = num;
    await Preferences.setPrizeSoundVolume(num);
  }
  //当選ベル音量を返す
  Future<double> _getPrizeSoundVolume() async {
    final double num = await Preferences.getPrizeSoundVolume() ?? 1.0;
    return num;
  }

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

  //getter/setter
  double get speakSoundVolume {
    return _speakSoundVolume;
  }
  set speakSoundVolume(double num) {
    _setSpeakSoundVolume(num);
  }
  //読み上げ音量を記録
  Future<void> _setSpeakSoundVolume(double num) async {
    _speakSoundVolume = num;
    await Preferences.setSpeakSoundVolume(num);
  }
  //読み上げ音量を返す
  Future<double> _getSpeakSoundVolume() async {
    final double num = await Preferences.getSpeakSoundVolume() ?? 1.0;
    return num;
  }

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

  //getter/setter
  String get candidateText {
    return _candidateText;
  }
  set candidateText(String str) {
    _setCandidateText(str);
  }
  List<int> get candidateNumbers {
    return _candidateNumbers;
  }
  //選択肢を記録
  Future<void> _setCandidateText(String str) async {
    str = _candidateFormat(str);
    _candidateText = str;
    _candidateNumbers = _makeCandidateNumbers(str);
    await Preferences.setCandidateText(str);
  }
  //選択肢デフォルト設定
  Future<void> setCandidateTextDefault() async {
    _candidateText = ConstValue.candidateTextDefault;
    _candidateNumbers = _makeCandidateNumbers(ConstValue.candidateTextDefault);
    await Preferences.setCandidateText(ConstValue.candidateTextDefault);
  }
  //選択肢を返す
  Future<String> _getCandidateText() async {
    final String str = await Preferences.getCandidateText() ?? ConstValue.candidateTextDefault;
    return str;
  }
  //選択肢を数字配列で返す
  Future<List<int>> _getCandidateNumbers() async {
    return _makeCandidateNumbers(await _getCandidateText());
  }
  //選択肢を数字配列にする
  List<int> _makeCandidateNumbers(str) {
    return _parseStrToNumbers(str);
  }

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

  //getter/setter
  String get prizeText {
    return _prizeText;
  }
  set prizeText(String str) {
    _setPrizeText(str);
  }
  List<Map<String,dynamic>> get prizeList {
    return _prizeList;
  }
  //賞を記録
  Future<void> _setPrizeText(String str) async {
    str = _prizeFormat(str);
    _prizeText = str;
    _prizeList = _makePrizeList(str);
    await Preferences.setPrizeText(str);
  }
  //賞デフォルト設定
  Future<void> setPrizeTextDefault() async {
    _prizeText = ConstValue.prizeTextDefault;
    _prizeList = _makePrizeList(ConstValue.prizeTextDefault);
    await Preferences.setPrizeText(ConstValue.prizeTextDefault);
  }
  //賞を返す
  Future<String> _getPrizeText() async {
    final String str = await Preferences.getPrizeText() ?? ConstValue.prizeTextDefault;
    return str;
  }
  //賞をListで返す
  Future<List<Map<String,dynamic>>> _getPrizeList() async {
    return _makePrizeList(await _getPrizeText());
  }
  //賞をListにする
  List<Map<String,dynamic>> _makePrizeList(String str) {
    List<Map<String,dynamic>> mapList = [];
    final List<String> lines = str.replaceAll('\r','').split('\n');
    for (int i = 0; i < lines.length; i++) {
      final List<String> ary = lines[i].split(':');
      final List<int> numbers = _parseStrToNumbers(ary[0]);
      final Map<String,dynamic> mapOne = {'numbers':numbers,'prize':ary[1]};
      mapList.add(mapOne);
    }
    return mapList;
  }

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

  //getter
  String get historyText {
    return _historyText;
  }
  set historyText(String str) {
    _setHistoryText(str);
  }
  List<int> get historyNumbers {
    return _historyNumbers;
  }
  //抽選結果を記録
  Future<void> _setHistoryText(String str) async {
    str = _historyFormat(str);
    _historyText = str;
    _historyNumbers = _makeHistoryNumbers(str);
    final String commaNum = _parseStrToNumbers(str).join(',');
    await Preferences.setHistoryText(commaNum);
  }
  //抽選結果デフォルト設定
  Future<void> setHistoryTextDefault() async {
    _historyText = ConstValue.historyTextDefault;
    _historyNumbers = _makeHistoryNumbers(ConstValue.historyTextDefault);
    await Preferences.setHistoryText(ConstValue.historyTextDefault);
  }
  //抽選結果を追加
  Future<bool> addHistoryText(int num) async {
    if (_historyNumbers.contains(num)) { //2重登録防止
      return false;
    }
    _historyNumbers.add(num);
    final String str = _historyNumbers.map((int num) => num.toString()).join(',');
    _historyText = str;
    await Preferences.setHistoryText(str);
    return true;
  }
  //抽選結果を返す
  Future<String> _getHistoryText() async {
    final String str = await Preferences.getHistoryText() ?? ConstValue.historyTextDefault;
    return str;
  }
  //抽選結果を数字配列で返す
  Future<List<int>> _getHistoryNumbers() async {
    return _makeHistoryNumbers(await _getHistoryText());
  }
  //抽選結果を数字配列にする
  List<int> _makeHistoryNumbers(str) {
    return _parseStrToNumbers(str);
  }

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

  //'1-10,12,15,17,20-50' などの文字列を数値配列に変換
  List<int> _parseStrToNumbers(String numString) {
    final List<String> numStrings = numString.split(',');
    List<int> numbers = [];
    for (String str in numStrings) {
      if (str.contains('-')) {
        List<String> ary = str.split('-');
        if (_isStringToIntParsable(ary[0]) && _isStringToIntParsable(ary[1])) {
          for (int i = int.parse(ary[0]); i <= int.parse(ary[1]); i++) {
            numbers.add(i);
          }
        }
      } else {
        if (_isStringToIntParsable(str)) {
          numbers.add(int.parse(str));
        }
      }
    }
    numbers = (Set<int>.from(numbers)).toList();	//unique
    return numbers;
  }
  //String を int に変換できるか
  bool _isStringToIntParsable(String str) {
    try {
      int.parse(str);
      return true;
    } catch (e) {
      return false;
    }
  }
  //カンマ区切り数字を整える
  List<int> _commaNumFormat(String input) {
    List<int> intList = [];
    final List<String> strList = input.split(',');
    for (String str in strList) {
      try {
        int num = int.parse(str);
        intList.add(num);
      } catch (e) {
        //何もしない
      }
    }
    return (Set<int>.from(intList)).toList();	//unique
  }
  //選択肢を整える。ユーザーの入力なので適宜調整する
  String _candidateFormat(String str) {
    str = str.replaceAll('0','0');
    str = str.replaceAll('1','1');
    str = str.replaceAll('2','2');
    str = str.replaceAll('3','3');
    str = str.replaceAll('4','4');
    str = str.replaceAll('5','5');
    str = str.replaceAll('6','6');
    str = str.replaceAll('7','7');
    str = str.replaceAll('8','8');
    str = str.replaceAll('9','9');
    str = str.replaceAll('、',',');
    str = str.replaceAll(',',',');
    str = str.replaceAll('ー','-');
    str = str.replaceAll('―','-');
    str = str.replaceAll(RegExp(r'[^0-9,-]'), '');
    str = str.replaceAll(RegExp(r',+'), ',');
    str = str.replaceAll(RegExp(r'\-+'), '-');
    return str;
  }
  //賞を整える。ユーザーの入力なので適宜調整する
  String _prizeFormat(String str) {
    final List<String> lines = str.replaceAll('\r','').split('\n');
    List<String> prizes = [];
    for (String str in lines) {
      str = str.replaceAll(':',':');
      if (str.contains(':') == false) {
        continue;
      }
      List<String> ary = str.split(':');
      ary[0] = ary[0].replaceAll('0','0');
      ary[0] = ary[0].replaceAll('1','1');
      ary[0] = ary[0].replaceAll('2','2');
      ary[0] = ary[0].replaceAll('3','3');
      ary[0] = ary[0].replaceAll('4','4');
      ary[0] = ary[0].replaceAll('5','5');
      ary[0] = ary[0].replaceAll('6','6');
      ary[0] = ary[0].replaceAll('7','7');
      ary[0] = ary[0].replaceAll('8','8');
      ary[0] = ary[0].replaceAll('9','9');
      ary[0] = ary[0].replaceAll('、',',');
      ary[0] = ary[0].replaceAll(',',',');
      ary[0] = ary[0].replaceAll('ー','-');
      ary[0] = ary[0].replaceAll('―','-');
      ary[0] = ary[0].replaceAll(RegExp(r'[^0-9,-]'), '');
      ary[0] = ary[0].replaceAll(RegExp(r',+'), ',');
      ary[0] = ary[0].replaceAll(RegExp(r'\-+'), '-');
      prizes.add('${ary[0]}:${ary[1]}');
    }
    return prizes.join('\n');
  }
  //抽選結果を整える。ユーザーの入力なので適宜調整する
  String _historyFormat(String str) {
    str = str.replaceAll('0','0');
    str = str.replaceAll('1','1');
    str = str.replaceAll('2','2');
    str = str.replaceAll('3','3');
    str = str.replaceAll('4','4');
    str = str.replaceAll('5','5');
    str = str.replaceAll('6','6');
    str = str.replaceAll('7','7');
    str = str.replaceAll('8','8');
    str = str.replaceAll('9','9');
    str = str.replaceAll('、',',');
    str = str.replaceAll(',',',');
    str = str.replaceAll(RegExp(r'[^0-9,]'), '');
    str = str.replaceAll(RegExp(r',+'), ',');
    return str;
  }

}

lib/audio_play.dart

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

import 'package:just_audio/just_audio.dart';

import 'package:lotteryslot/const_value.dart';

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

  double _machineSoundVolume = 1.0;
  double _prizeSoundVolume = 1.0;

  //constructor
  AudioPlay() {
    _initial();
  }
  void _initial() async {
    for (int i = 0; i < _playerMachineStart.length; i++) {
      await _playerMachineStart[i].setAsset(ConstValue.audioMachineStart);
    }
    for (int i = 0; i < _playerMachineStop.length; i++) {
      await _playerMachineStop[i].setAsset(ConstValue.audioMachineStop);
    }
    for (int i = 0; i < _playerPrize.length; i++) {
      await _playerPrize[i].setAsset(ConstValue.audioPrize);
    }
  }
  void dispose() {
    for (int i = 0; i < _playerMachineStart.length; i++) {
      _playerMachineStart[i].dispose();
    }
    for (int i = 0; i < _playerMachineStop.length; i++) {
      _playerMachineStop[i].dispose();
    }
    for (int i = 0; i < _playerPrize.length; i++) {
      _playerPrize[i].dispose();
    }
  }
  //setter
  set machineSoundVolume(double vol) {
    _machineSoundVolume = vol;
  }
  set prizeSoundVolume(double vol) {
    _prizeSoundVolume = vol;
  }
  //
  void playMachineStart() async {
    _playerMachineStartPtr += 1;
    if (_playerMachineStartPtr >= _playerMachineStart.length) {
      _playerMachineStartPtr = 0;
    }
    await _playerMachineStart[_playerMachineStartPtr].setVolume(_machineSoundVolume);
    await _playerMachineStart[_playerMachineStartPtr].pause();
    await _playerMachineStart[_playerMachineStartPtr].seek(Duration.zero);
    await _playerMachineStart[_playerMachineStartPtr].play();
  }
  void playMachineStop() async {
    _playerMachineStopPtr += 1;
    if (_playerMachineStopPtr >= _playerMachineStop.length) {
      _playerMachineStopPtr = 0;
    }
    await _playerMachineStop[_playerMachineStopPtr].setVolume(_machineSoundVolume);
    await _playerMachineStop[_playerMachineStopPtr].pause();
    await _playerMachineStop[_playerMachineStopPtr].seek(Duration.zero);
    await _playerMachineStop[_playerMachineStopPtr].play();
  }
  void playPrize() async {
    _playerPrizePtr += 1;
    if (_playerPrizePtr >= _playerPrize.length) {
      _playerPrizePtr = 0;
    }
    await _playerPrize[_playerPrizePtr].setVolume(_prizeSoundVolume);
    await _playerPrize[_playerPrizePtr].pause();
    await _playerPrize[_playerPrizePtr].seek(Duration.zero);
    await _playerPrize[_playerPrizePtr].play();
  }
}

lib/text_to_speech.dart

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

import 'package:flutter_tts/flutter_tts.dart';

//音声読み上げ
class TextToSpeech {

  late FlutterTts _flutterTts;

  //constructor
  TextToSpeech() {
    _flutterTts = FlutterTts();
  }

  void setVoice(Map<String,String> voiceMap) {
    _flutterTts.setVoice(voiceMap);
  }

  void setVolume(double volume) {
    _flutterTts.setVolume(volume);
  }

  void speak(String str) {
    _flutterTts.speak(str);
  }

  Future<List<String>> getSpeakVoices() async {
    List<String> speakVoices = [];
    speakVoices.add("   ");
    List<Object?> ttsVoices = await _flutterTts.getVoices;
    var availableVoices = ttsVoices.cast<Map>().map((e) => e.cast<String,String>()).toList();
    for (var voice in availableVoices) {
      speakVoices.add("${voice['locale']} ${voice['name']}");
    }
    speakVoices.sort();
    return speakVoices;
  }

  Future<void> setSpeakVoiceFromIndex(int speakVoiceIndex) async {
    List<String> speakVoices = await getSpeakVoices();
    if (speakVoices.length <= speakVoiceIndex) {
      speakVoiceIndex = 0;
    }
    List<String> ary = speakVoices[speakVoiceIndex].split(' ');
    Map<String,String> voiceMap = {'locale':ary[0],'name':ary[1]};
    setVoice(voiceMap);
  }

}

lib/page_state.dart

///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-05
///

//現在のページを記録。initStateでタイミングが合わない時にbuild内で一度だけ実行させるために使用。
class PageState {

  static String _currentPage = '';

  static void setCurrentPage(String str) {
    _currentPage = str;
  }

  static String getCurrentPage() {
    return _currentPage;
  }

}

lib/language_state.dart

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

import 'package:lotteryslot/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/tts_state.dart

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

import 'package:lotteryslot/preferences.dart';

class TtsState {

  static int _speakVoiceIndex = 0;

  //音声のインデックスを記録
  static Future<void> setSpeakVoiceIndex(int num) async {
    _speakVoiceIndex = num;
    await Preferences.setSpeakVoiceIndex(_speakVoiceIndex);
  }
  //音声のインデックスを返す
  static Future<int> getSpeakVoiceIndex() async {
    _speakVoiceIndex = await Preferences.getSpeakVoiceIndex();
    return _speakVoiceIndex;
  }

}

lib/version_state.dart

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

class VersionState {

  static String _version = '';

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

}

lib/preferences.dart

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

import 'package:shared_preferences/shared_preferences.dart';

import 'package:lotteryslot/const_value.dart';

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

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

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

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

  //声インデックス
  static Future<void> setSpeakVoiceIndex(int num) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt(ConstValue.prefSpeakVoiceIndex, num);
  }
  static Future<int> getSpeakVoiceIndex() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int num = prefs.getInt(ConstValue.prefSpeakVoiceIndex) ?? 0;
    return num;
  }

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

  //抽選候補 入力テキスト
  static Future<void> setCandidateText(String str) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(ConstValue.prefCandidateText, str);
  }
  static Future<String?> getCandidateText() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String? str = prefs.getString(ConstValue.prefCandidateText);
    return str;
  }

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

  //賞 入力テキスト
  static Future<void> setPrizeText(String str) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(ConstValue.prefPrizeText, str);
  }
  static Future<String?> getPrizeText() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String? str = prefs.getString(ConstValue.prefPrizeText);
    return str;
  }

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

  //抽選履歴 入力テキスト
  static Future<void> setHistoryText(String str) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(ConstValue.prefHistoryText, str);
  }
  static Future<String?> getHistoryText() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String? str = prefs.getString(ConstValue.prefHistoryText);
    return str;
  }

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

  //背景画像インデックス
  static Future<void> setMachineImageIndex(int num) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt(ConstValue.prefMachineImageIndex, num);
  }
  static Future<int> getMachineImageIndex() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int num = prefs.getInt(ConstValue.prefMachineImageIndex) ?? 0;
    return num;
  }

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

  //抽選履歴をメイン画面に表示するか否かのflag
  static Future<void> setHistoryDrawFlag(bool flag) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setBool(ConstValue.prefHistoryDrawFlag, flag);
  }
  static Future<bool> getHistoryDrawFlag() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final bool flag = prefs.getBool(ConstValue.prefHistoryDrawFlag) ?? true;
    return flag;
  }

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

  //抽選機の速度
  static Future<void> setMachineSpeed(int num) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt(ConstValue.prefMachineSpeed, num);
  }
  static Future<int> getMachineSpeed() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int num = prefs.getInt(ConstValue.prefMachineSpeed) ?? 1;
    return num;
  }

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

  //抽選機動作音の音量
  static Future<void> setMachineSoundVolume(double num) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setDouble(ConstValue.prefMachineSoundVolume, num);
  }
  static Future<double> getMachineSoundVolume() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final double num = prefs.getDouble(ConstValue.prefMachineSoundVolume) ?? 1.0;
    return num;
  }

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

  //賞表示の音量
  static Future<void> setPrizeSoundVolume(double num) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setDouble(ConstValue.prefPrizeSoundVolume, num);
  }
  static Future<double> getPrizeSoundVolume() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final double num = prefs.getDouble(ConstValue.prefPrizeSoundVolume) ?? 1.0;
    return num;
  }

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

  //読み上げの音量
  static Future<void> setSpeakSoundVolume(double num) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setDouble(ConstValue.prefSpeakSoundVolume, num);
  }
  static Future<double> getSpeakSoundVolume() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final double num = prefs.getDouble(ConstValue.prefSpeakSoundVolume) ?? 1.0;
    return num;
  }

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

}

lib/const_value.dart

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

import 'package:flutter/material.dart';

class ConstValue {
  //pref
  static const String prefLanguageCode = 'languageCode';
  static const String prefSpeakVoiceIndex = 'speakVoiceIndex';
  static const String prefCandidateText = 'candidateTexts';
  static const String prefPrizeText = 'prizeTexts';
  static const String prefHistoryText = 'historyTexts';
  static const String prefHistoryDrawFlag = 'historyDrawFlag';
  static const String prefMachineImageIndex = 'machineImageIndex';
  static const String prefMachineSpeed = 'machineSpeed';
  static const String prefMachineSoundVolume = 'machineSoundVolume';
  static const String prefPrizeSoundVolume = 'prizeSoundVolume';
  static const String prefSpeakSoundVolume = 'speakSoundVolume';
  //default
  static const String candidateTextDefault = '1-100';
  static const String prizeTextDefault = '1:Space travel\n2:Round-the-world trip\n3:Luxury sports car\n4-10:Coffee ticket\n11,22,33,44,55,66,77,88,99:Smartphone\n100:Laptop computer';
  static const String historyTextDefault = '';
  //image
  static const List<String> machineImages = [
    'assets/image/machine.webp',
    'assets/image/machine02.webp',
    'assets/image/machine03.webp',
    'assets/image/machine04.webp',
  ];
  static const String imageMachineOver = 'assets/image/machine_over.webp';
  static const String imageReel = 'assets/image/reel.webp';
  //color
  static const Color colorButtonBack = Color.fromARGB(255, 173,42,0);
  static const Color colorButtonFore = Color.fromARGB(255, 255, 255, 255);
  static const Color colorBack = Color.fromARGB(255, 97,24,0);
  static const Color colorSettingAccent = Color.fromARGB(255, 76,43,164);
  //sound
  static const String audioMachineStart = 'assets/sound/reel_start.wav';
  static const String audioMachineStop = 'assets/sound/reel_stop.wav';
  static const String audioPrize = 'assets/sound/bell.wav';
  //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/ad_mob.dart

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

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-0000000000000000/0000000000';
      _isAdMob = true;
    } else if (!kIsWeb && Platform.isIOS) {
      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/l10n/app_bg.arb

{
	"@@locale":"bg",
	"@locale": {
		"description": "ブルガリア"
	},
	"start": "СТАРТ",
    "setting": "Настройка",
    "candidate": "Кандидат",
    "prize": "награда",
    "history": "История",
    "historyMainDraw": "Показване на лотарийни истории на главната страница",
    "initial": "Първоначално",
    "erase": "Изтрива",

	"machineImageIndex": "Изображение на машината",
    "machineSpeed": "Скорост на машината за лотария",
    "machineSoundVolume": "Сила на звука на машината",
    "prizeSoundVolume": "Награда сила на звука",
    "speakSoundVolume": "говорете силата на звука",
    "speakResult": "Прочетете резултатите от лотарията",
    "voice": "глас",
    "language": "език",
    "usage1": "Кандидат:\nПосочете избора на лотария чрез цифров диапазон и числа, разделени със запетаи.",
    "usage2": "Награда:\nВъведете числовия диапазон, стойностите, разделени със запетая, и името на наградата, като ги свържете с : (двоеточие). Може да се напише на няколко реда.",
    "usage3": "История:\nАко искате да започнете лотарията от средата, въведете числата, разделени със запетаи.",
    "usage4": "Пример за числена спецификация) 1000-1200,1300,1400,1500 представляват общо 204 елемента, включително 201 елемента от 1000 до 1200 и 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_cs.arb

{
	"@@locale":"cs",
	"@locale": {
		"description": "チェコ"
	},
	"start": "START",
    "setting": "Nastavení",
    "candidate": "Kandidát",
    "prize": "Cena",
    "history": "Dějiny",
    "historyMainDraw": "Zobrazit historii loterií na hlavní stránce",
    "initial": "Počáteční",
    "erase": "Vymazat",

	"machineImageIndex": "Obraz stroje",
    "machineSpeed": "Rychlost loterijního stroje",
    "machineSoundVolume": "Hlasitost zvuku stroje",
    "prizeSoundVolume": "Hlasitost zvuku ceny",
    "speakSoundVolume": "mluvit hlasitost zvuku",
    "speakResult": "Přečtěte si výsledky loterie",
    "voice": "Hlas",
    "language": "Jazyk",
    "usage1": "Kandidát:\nUpřesněte volby loterie pomocí číselného rozsahu a čísel oddělených čárkami.",
    "usage2": "Cena:\nZadejte číselný rozsah, hodnoty oddělené čárkou a název ceny tak, že je spojíte se : (dvojtečkou). Lze psát na více řádků.",
    "usage3": "Historie:\nPokud chcete spustit loterii od středu, zadejte čísla oddělená čárkami.",
    "usage4": "Příklad číselné specifikace) 1000-1200,1300,1400,1500 představuje celkem 204 položek, včetně 201 položek od 1000 do 1200 a 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_da.arb

{
	"@@locale":"da",
	"@locale": {
		"description": "デンマーク"
	},
	"start": "START",
    "setting": "Indstilling",
    "candidate": "Kandidat",
    "prize": "Præmie",
    "history": "Historie",
    "historyMainDraw": "Vis lotterihistorier på hovedsiden",
    "initial": "Initial",
    "erase": "Slette",

	"machineImageIndex": "Maskinbillede",
    "machineSpeed": "Lotteri maskine hastighed",
    "machineSoundVolume": "Maskinens lydstyrke",
    "prizeSoundVolume": "Pris lydstyrke",
    "speakSoundVolume": "tale lydstyrke",
    "speakResult": "Læs lotteriets resultater",
    "voice": "Stemme",
    "language": "Sprog",
    "usage1": "Kandidat:\nSpecificer lotterivalgene efter numerisk rækkevidde og kommaseparerede tal.",
    "usage2": "Præmie:\nIndtast det numeriske område, kommaseparerede værdier og præmienavnet ved at forbinde dem med et : (kolon). Kan skrives på flere linjer.",
    "usage3": "Historik:\nHvis du vil starte lotteriet fra midten, skal du indtaste tal adskilt med kommaer.",
    "usage4": "Eksempel på numerisk specifikation) 1000-1200,1300,1400,1500 repræsenterer i alt 204 varer, herunder 201 varer fra 1000 til 1200 og 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_de.arb

{
	"@@locale":"de",
	"@locale": {
		"description": "ドイツ"
	},
	"start": "START",
    "setting": "Einstellung",
    "candidate": "Kandidat",
    "prize": "Preis",
    "history": "Geschichte",
    "historyMainDraw": "Lotterieverläufe auf der Hauptseite anzeigen",
    "initial": "Anfänglich",
    "erase": "Löschen",

	"machineImageIndex": "Maschinenbild",
    "machineSpeed": "Geschwindigkeit des Lotterieautomaten",
    "machineSoundVolume": "Lautstärke des Maschinengeräuschs",
    "prizeSoundVolume": "Preisliche Lautstärke",
    "speakSoundVolume": "Lautstärke sprechen",
    "speakResult": "Lesen Sie die Lottoergebnisse vor",
    "voice": "Stimme",
    "language": "Sprache",
    "usage1": "Kandidat:\nGeben Sie die Lotterieauswahl nach Zahlenbereich und durch Kommas getrennten Zahlen an.",
    "usage2": "Preis:\nGeben Sie den Zahlenbereich, durch Kommas getrennte Werte und den Namen des Preises ein, indem Sie diese mit einem : (Doppelpunkt) verbinden. Kann in mehreren Zeilen geschrieben werden.",
    "usage3": "Verlauf:\nWenn Sie die Lotterie in der Mitte beginnen möchten, geben Sie durch Kommas getrennte Zahlen ein.",
    "usage4": "Beispiel einer numerischen Angabe: 1000-1200, 1300, 1400, 1500 stellt insgesamt 204 Elemente dar, darunter 201 Elemente von 1000 bis 1200 und 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_el.arb

{
	"@@locale":"el",
	"@locale": {
		"description": "ギリシャ"
	},
	"start": "ΑΡΧΗ",
    "setting": "Σύνθεση",
    "candidate": "Υποψήφιος",
    "prize": "Βραβείο",
    "history": "Ιστορία",
    "historyMainDraw": "Εμφάνιση ιστοριών λαχειοφόρων αγορών στην κύρια σελίδα",
    "initial": "Αρχικός",
    "erase": "Εξάλειψη",

	"machineImageIndex": "Εικόνα μηχανής",
    "machineSpeed": "Ταχύτητα μηχανής λοταρίας",
    "machineSoundVolume": "Ένταση ήχου μηχανής",
    "prizeSoundVolume": "Ένταση ήχου έπαθλο",
    "speakSoundVolume": "εκφώνηση έντασης ήχου",
    "speakResult": "Διαβάστε τα αποτελέσματα της κλήρωσης",
    "voice": "Φωνή",
    "language": "Γλώσσα",
    "usage1": "Υποψήφιος:\nΚαθορίστε τις επιλογές λοταρίας με αριθμητικό εύρος και αριθμούς διαχωρισμένους με κόμματα.",
    "usage2": "Βραβείο:\nΕισαγάγετε το αριθμητικό εύρος, τις τιμές διαχωρισμένες με κόμμα και το όνομα του βραβείου συνδέοντάς τα με ένα : (άνω τελεία). Μπορεί να γραφτεί σε πολλές γραμμές.",
    "usage3": "Ιστορικό:\nΑν θέλετε να ξεκινήσετε τη λαχειοφόρο αγορά από τη μέση, εισαγάγετε αριθμούς διαχωρισμένους με κόμμα.",
    "usage4": "Παράδειγμα αριθμητικής προδιαγραφής) 1000-1200,1300,1400,1500 αντιπροσωπεύει ένα σύνολο 204 ειδών, συμπεριλαμβανομένων 201 στοιχείων από 1000 έως 1200 και 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_en.arb

{
	"@@locale":"en",
	"@locale": {
		"description": "英語"
	},
	"start": "START",
	"setting": "Setting",
	"candidate": "Candidate",
	"prize": "Prize",
	"history": "History",
	"historyMainDraw": "Display lottery histories on main page",
	"initial": "Initial",
	"erase": "Erase",

	"machineImageIndex": "Machine image",
	"machineSpeed": "Lottery machine speed",
	"machineSoundVolume": "Machine sound volume",
	"prizeSoundVolume": "Prize sound volume",
	"speakSoundVolume": "speak sound volume",
	"speakResult": "Read out the lottery results",
	"voice": "Voice",
	"language": "Language",

	"usage1": "Candidate:\nSpecify the lottery choices by numerical range and comma-separated numbers.",
	"usage2": "Prize:\nEnter the numerical range, comma-separated values, and prize name by connecting them with a : (colon). Can be written on multiple lines.",
	"usage3": "History:\nIf you want to start the lottery from the middle, enter numbers separated by commas.",
	"usage4": "Example of numerical specification) 1000-1200,1300,1400,1500 represents a total of 204 items, including 201 items from 1000 to 1200 and 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_es.arb

{
	"@@locale":"es",
	"@locale": {
		"description": "スペイン"
	},
	"start": "COMENZAR",
    "setting": "Configuración",
    "candidate": "Candidato",
    "prize": "Premio",
    "history": "Historia",
    "historyMainDraw": "Mostrar historiales de lotería en la página principal",
    "initial": "Inicial",
    "erase": "Borrar",

  	"machineImageIndex": "Imagen de la máquina",
    "machineSpeed": "Velocidad de la máquina de lotería",
    "machineSoundVolume": "Volumen del sonido de la máquina",
    "prizeSoundVolume": "Volumen del sonido del premio",
    "speakSoundVolume": "hablar volumen de sonido",
    "speakResult": "Lea los resultados de la lotería",
    "voice": "Voz",
    "language": "Idioma",
    "usage1": "Candidato:\nEspecifique las opciones de lotería por rango numérico y números separados por comas.",
    "usage2": "Premio:\nIngrese el rango numérico, los valores separados por comas y el nombre del premio conectándolos con : (dos puntos). Se puede escribir en varias líneas.",
    "usage3": "Historial:\nSi desea comenzar la lotería desde el medio, ingrese los números separados por comas.",
    "usage4": "Ejemplo de especificación numérica) 1000-1200,1300,1400,1500 representa un total de 204 artículos, incluidos 201 artículos de 1000 a 1200 y 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_et.arb

{
	"@@locale":"et",
	"@locale": {
		"description": "エストニア"
	},
	"start": "START",
    "setting": "Seadistamine",
    "candidate": "kandidaat",
    "prize": "Auhind",
    "history": "Ajalugu",
    "historyMainDraw": "Kuva avalehel loterii ajalugu",
    "initial": "Esialgne",
    "erase": "Kustuta",

	"machineImageIndex": "Masina pilt",
    "machineSpeed": "Loterii masina kiirus",
    "machineSoundVolume": "Masina helitugevus",
    "prizeSoundVolume": "Auhinna helitugevus",
    "speakSoundVolume": "räägi helitugevust",
    "speakResult": "Lugege ette loterii tulemused",
    "voice": "Hääl",
    "language": "Keel",
    "usage1": "Kandidaat:\nTäpsustage loterii valikud numbrivahemiku ja komadega eraldatud numbrite abil.",
    "usage2": "Auhind:\nSisestage numbrivahemik, komadega eraldatud väärtused ja auhinna nimi, ühendades need märgiga : (koolon). Võib kirjutada mitmele reale.",
    "usage3": "Ajalugu:\nKui soovite loosimist alustada keskelt, sisestage numbrid komadega eraldatuna.",
    "usage4": "Numbrilise spetsifikatsiooni näide) 1000-1200,1300,1400,1500 tähistab kokku 204 üksust, sealhulgas 201 üksust vahemikus 1000 kuni 1200 ja 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_fi.arb

{
	"@@locale":"fi",
	"@locale": {
		"description": "フィンランド"
	},
	"start": "ALKAA",
    "setting": "Asetus",
    "candidate": "ehdokas",
    "prize": "Palkinto",
    "history": "Historia",
    "historyMainDraw": "Näytä lottohistoriat pääsivulla",
    "initial": "Alkukirjain",
    "erase": "Poista",

	"machineImageIndex": "Koneen kuva",
    "machineSpeed": "Lottokoneen nopeus",
    "machineSoundVolume": "Koneen äänenvoimakkuus",
    "prizeSoundVolume": "Palkinnon äänenvoimakkuus",
    "speakSoundVolume": "puhua äänenvoimakkuutta",
    "speakResult": "Lue lottotulokset",
    "voice": "Ääni",
    "language": "Kieli",
    "usage1": "Ehdokas:\nMääritä lottovalinnat numeroalueen ja pilkuilla eroteltujen numeroiden mukaan.",
    "usage2": "Palkinto:\nAnna numeroalue, pilkuilla erotetut arvot ja palkinnon nimi yhdistämällä ne :llä (kaksoispiste). Voidaan kirjoittaa usealle riville.",
    "usage3": "Historia:\nJos haluat aloittaa arpajaiset keskeltä, syötä numerot pilkuilla erotettuina.",
    "usage4": "Esimerkki numeerisesta määrityksestä) 1000-1200,1300,1400,1500 edustaa yhteensä 204 kohdetta, mukaan lukien 201 kohdetta välillä 1000-1200 ja 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_fr.arb

{
	"@@locale":"fr",
	"@locale": {
		"description": "フランス"
	},
	"start": "COMMENCER",
    "setting": "Paramètre",
    "candidate": "Candidat",
    "prize": "Prix",
    "history": "Histoire",
    "historyMainDraw": "Afficher les historiques de loterie sur la page principale",
    "initial": "Initial",
    "erase": "Effacer",

	"machineImageIndex": "Image de la machine",
    "machineSpeed": "Vitesse de la machine de loterie",
    "machineSoundVolume": "Volume sonore des machines",
    "prizeSoundVolume": "Volume sonore du prix",
    "speakSoundVolume": "parler le volume sonore",
    "speakResult": "Lisez les résultats de la loterie",
    "voice": "Voix",
    "language": "Langue",
    "usage1": "Candidat:\nSpécifiez les choix de loterie par plage numérique et par nombres séparés par des virgules.",
    "usage2": "Prix:\nEntrez la plage numérique, les valeurs séparées par des virgules et le nom du prix en les reliant par : (deux-points). Peut être écrit sur plusieurs lignes.",
    "usage3": "Historique:\nSi vous souhaitez démarrer la loterie par le milieu, saisissez les nombres séparés par des virgules.",
    "usage4": "Exemple de spécification numérique) 1000-1200,1300,1400,1500 représente un total de 204 éléments, dont 201 éléments de 1000 à 1200 et 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_hu.arb

{
	"@@locale":"hu",
	"@locale": {
		"description": "ハンガリー"
	},
	"start": "RAJT",
    "setting": "Beállítás",
    "candidate": "Jelölt",
    "prize": "Díj",
    "history": "Történelem",
    "historyMainDraw": "Lottótörténetek megjelenítése a főoldalon",
    "initial": "A kezdeti",
    "erase": "Törli",

	"machineImageIndex": "Gép kép",
    "machineSpeed": "Lottógép sebessége",
    "machineSoundVolume": "A gép hangereje",
    "prizeSoundVolume": "Nyeremény hangerő",
    "speakSoundVolume": "beszéljen hangerőt",
    "speakResult": "Olvassa el a lottó eredményét",
    "voice": "Hang",
    "language": "Nyelv",
    "usage1": "Jelölt:\nAdja meg a sorsolási lehetőségeket numerikus tartománnyal és vesszővel elválasztott számokkal.",
    "usage2": "Nyeremény:\nAdja meg a numerikus tartományt, a vesszővel elválasztott értékeket és a nyeremény nevét a : (kettőspont) jellel összekapcsolva. Több sorba is írható.",
    "usage3": "Előzmények:\nHa középről szeretné kezdeni a sorsolást, adja meg a számokat vesszővel elválasztva.",
    "usage4": "Példa a numerikus specifikációra) Az 1000-1200,1300,1400,1500 összesen 204 tételt jelent, köztük 201 tételt 1000-től 1200-ig és 1300-tól 1400-ig, 1500-ig.",

	"dummy": "dummy"
}

lib/l10n/app_it.arb

{
	"@@locale":"it",
	"@locale": {
		"description": "イタリア"
	},
	"start": "INIZIO",
    "setting": "Collocamento",
    "candidate": "Candidato",
    "prize": "Premio",
    "history": "Storia",
    "historyMainDraw": "Visualizza la cronologia della lotteria sulla pagina principale",
    "initial": "Iniziale",
    "erase": "Cancellare",

	"machineImageIndex": "Immagine della macchina",
    "machineSpeed": "Velocità della macchina della lotteria",
    "machineSoundVolume": "Volume del suono della macchina",
    "prizeSoundVolume": "Volume del suono del premio",
    "speakSoundVolume": "parlare a volume",
    "speakResult": "Leggi i risultati della lotteria",
    "voice": "Voce",
    "language": "Lingua",
    "usage1": "Candidato:\nSpecificare le scelte della lotteria in base all'intervallo numerico e ai numeri separati da virgole.",
    "usage2": "Premio:\nInserisci l'intervallo numerico, i valori separati da virgole e il nome del premio collegandoli con : (due punti). Può essere scritto su più righe.",
    "usage3": "Cronologia:\nSe vuoi iniziare la lotteria dal centro, inserisci i numeri separati da virgole.",
    "usage4": "Esempio di specifica numerica) 1000-1200,1300,1400,1500 rappresenta un totale di 204 voci, incluse 201 voci da 1000 a 1200 e 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_ja.arb

{
	"@@locale":"ja",
	"@locale": {
		"description": "日本"
	},
	"start": "START",
	"setting": "設定",
	"candidate": "抽選対象",
	"prize": "賞",
	"history": "抽選履歴",
	"historyMainDraw": "抽選履歴をメインページに表示",
	"initial": "初期値",
	"erase": "消去",

	"machineImageIndex": "機械画像",
	"machineSpeed": "抽選機の速度",
	"machineSoundVolume": "抽選機の音量",
	"prizeSoundVolume": "当選ベルの音量",
	"speakSoundVolume": "発声の音量",
	"speakResult": "抽選結果を読み上げます",
	"voice": "声",
	"language": "表示言語",

	"usage1": "抽選対象:\n数値範囲およびカンマ区切り数値で指定します。",
	"usage2": "賞:\n数値範囲およびカンマ区切り数値、そして賞名を:(コロン)で繋げて記述します。複数行記述できます。",
	"usage3": "抽選履歴:\n抽選を途中から始めたい場合はカンマ区切り数値を入力します。",
	"usage4": "数値指定の例) 1000-1200,1300,1400,1500 は1000から1200までの201個と1300,1400,1500の合計204個を表します。",

	"dummy": "dummy"
}

lib/l10n/app_lt.arb

{
	"@@locale":"lt",
	"@locale": {
		"description": "リトアニア"
	},
	"start": "PRADĖTI",
    "setting": "Nustatymas",
    "candidate": "Kandidatas",
    "prize": "Prizas",
    "history": "Istorija",
    "historyMainDraw": "Rodyti loterijų istorijas pagrindiniame puslapyje",
    "initial": "Pradinis",
    "erase": "Ištrinti",

	"machineImageIndex": "Mašinos vaizdas",
    "machineSpeed": "Loterijos mašinos greitis",
    "machineSoundVolume": "Mašinos garso stiprumas",
    "prizeSoundVolume": "Prizas garso garsumas",
    "speakSoundVolume": "kalbėti garsu",
    "speakResult": "Perskaitykite loterijos rezultatus",
    "voice": "Balsas",
    "language": "Kalba",
    "usage1": "Kandidatas:\nNurodykite loterijos pasirinkimus pagal skaičių diapazoną ir kableliais atskirtus skaičius.",
    "usage2": "Prizas:\nĮveskite skaičių diapazoną, kableliais atskirtas reikšmes ir prizo pavadinimą, sujungdami juos su : (dvitaškiu). Galima rašyti keliose eilutėse.",
    "usage3": "Istorija:\nJei norite pradėti loteriją nuo vidurio, įveskite skaičius, atskirtus kableliais.",
    "usage4": "Skaitmeninės specifikacijos pavyzdys) 1000-1200,1300,1400,1500 reiškia iš viso 204 elementus, įskaitant 201 elementą nuo 1000 iki 1200 ir 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_lv.arb

{
	"@@locale":"lv",
	"@locale": {
		"description": "ラトビア"
	},
	"start": "SĀKT",
    "setting": "Iestatījums",
    "candidate": "Kandidāts",
    "prize": "Balva",
    "history": "Vēsture",
    "historyMainDraw": "Parādīt loterijas vēsturi galvenajā lapā",
    "initial": "Sākotnējais",
    "erase": "Dzēst",

	"machineImageIndex": "Mašīnas attēls",
    "machineSpeed": "Loterijas automāta ātrums",
    "machineSoundVolume": "Mašīnas skaņas skaļums",
    "prizeSoundVolume": "Balvas skaņas skaļums",
    "speakSoundVolume": "runāt skaņas skaļumu",
    "speakResult": "Izlasiet loterijas rezultātus",
    "voice": "Balss",
    "language": "Valoda",
    "usage1": "Kandidāts:\nNorādiet loterijas izvēli, izmantojot ciparu diapazonu un ar komatu atdalītiem skaitļiem.",
    "usage2": "Balva:\nIevadiet skaitlisko diapazonu, komatatdalītās vērtības un balvas nosaukumu, savienojot tos ar : (kolu). Var rakstīt vairākās rindās.",
    "usage3": "Vēsture:\nJa vēlaties sākt loteriju no vidus, ievadiet skaitļus, atdalot tos ar komatiem.",
    "usage4": "Skaitliskās specifikācijas piemērs) 1000-1200,1300,1400,1500 kopā apzīmē 204 vienumus, tostarp 201 vienumu no 1000 līdz 1200 un 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_nl.arb

{
	"@@locale":"nl",
	"@locale": {
		"description": "オランダ"
	},
	"start": "BEGIN",
    "setting": "Instelling",
    "candidate": "Kandidaat",
    "prize": "Prijs",
    "history": "Geschiedenis",
    "historyMainDraw": "Toon loterijgeschiedenissen op de hoofdpagina",
    "initial": "Voorletter",
    "erase": "Wissen",

   	"machineImageIndex": "Machinebeeld",
    "machineSpeed": "Snelheid loterijmachine",
    "machineSoundVolume": "Volume van machinegeluid",
    "prizeSoundVolume": "Prijs geluidsvolume",
    "speakSoundVolume": "spreek geluidsvolume",
    "speakResult": "Lees de loterijresultaten voor",
    "voice": "Stem",
    "language": "Taal",
    "usage1": "Kandidaat:\nSpecificeer de loterijkeuzes op basis van numeriek bereik en door komma's gescheiden getallen.",
    "usage2": "Prijs:\nVoer het numerieke bereik, de door komma's gescheiden waarden en de prijsnaam in door ze te verbinden met een : (dubbele punt). Kan op meerdere regels geschreven worden.",
    "usage3": "Geschiedenis:\nAls u de loterij vanuit het midden wilt starten, voert u de getallen in, gescheiden door komma's.",
    "usage4": "Voorbeeld van numerieke specificatie) 1000-1200,1300,1400,1500 vertegenwoordigt een totaal van 204 items, inclusief 201 items van 1000 tot 1200 en 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_pl.arb

{
	"@@locale":"pl",
	"@locale": {
		"description": "ポーランド"
	},
	"start": "POCZĄTEK",
    "setting": "Ustawienie",
    "candidate": "Kandydat",
    "prize": "Nagroda",
    "history": "Historia",
    "historyMainDraw": "Wyświetlaj historie loterii na stronie głównej",
    "initial": "Wstępny",
    "erase": "Usuwać",

	"machineImageIndex": "Obraz maszyny",
    "machineSpeed": "Prędkość maszyny loteryjnej",
    "machineSoundVolume": "Głośność dźwięku maszyny",
    "prizeSoundVolume": "Nagroda za głośność dźwięku",
    "speakSoundVolume": "mówić głośność",
    "speakResult": "Przeczytaj wyniki loterii",
    "voice": "Głos",
    "language": "Język",
    "usage1": "Kandydat:\nOkreśl wybrane loterie za pomocą zakresu liczbowego i liczb oddzielonych przecinkami.",
    "usage2": "Nagroda:\nWprowadź zakres liczbowy, wartości oddzielone przecinkami i nazwę nagrody, łącząc je za pomocą : (dwukropka). Można pisać w wielu wierszach.",
    "usage3": "Historia:\nJeśli chcesz rozpocząć loterię od środka, wprowadź liczby oddzielone przecinkami.",
    "usage4": "Przykład zestawienia numerycznego) 1000-1200,1300,1400,1500 oznacza łącznie 204 pozycje, w tym 201 pozycji od 1000 do 1200 oraz 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_pt.arb

{
	"@@locale":"pt",
	"@locale": {
		"description": "ポルトガル"
	},
	"start": "COMEÇAR",
    "setting": "Contexto",
    "candidate": "Candidato",
    "prize": "Prêmio",
    "history": "História",
    "historyMainDraw": "Exibir históricos de loteria na página principal",
    "initial": "Inicial",
    "erase": "Apagar",

	"machineImageIndex": "Imagem da máquina",
    "machineSpeed": "Velocidade da máquina de loteria",
    "machineSoundVolume": "Volume de som da máquina",
    "prizeSoundVolume": "Volume do som do prêmio",
    "speakSoundVolume": "falar o volume do som",
    "speakResult": "Leia os resultados da loteria",
    "voice": "Voz",
    "language": "Linguagem",
    "usage1": "Candidato:\nEspecifique as opções de loteria por intervalo numérico e números separados por vírgula.",
    "usage2": "Prêmio:\nInsira o intervalo numérico, os valores separados por vírgula e o nome do prêmio conectando-os com : (dois pontos). Pode ser escrito em várias linhas.",
    "usage3": "Histórico:\nSe você quiser começar a loteria do meio, insira os números separados por vírgulas.",
    "usage4": "Exemplo de especificação numérica) 1000-1200,1300,1400,1500 representa um total de 204 itens, incluindo 201 itens de 1000 a 1200 e 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_ro.arb

{
	"@@locale":"ro",
	"@locale": {
		"description": "ルーマニア"
	},
	"start": "START",
    "setting": "Setare",
    "candidate": "Candidat",
    "prize": "Premiu",
    "history": "Istorie",
    "historyMainDraw": "Afișați istoricul loteriei pe pagina principală",
    "initial": "Iniţială",
    "erase": "Şterge",

	"machineImageIndex": "Imaginea mașinii",
    "machineSpeed": "Viteza aparatului de loterie",
    "machineSoundVolume": "Volumul sunetului aparatului",
    "prizeSoundVolume": "Volumul sunetului premiat",
    "speakSoundVolume": "rostiți volumul sunetului",
    "speakResult": "Citiți rezultatele loteriei",
    "voice": "Voce",
    "language": "Limba",
    "usage1": "Candidat:\nSpecificați opțiunile de loterie după intervalul numeric și numerele separate prin virgulă.",
    "usage2": "Premiu:\nIntroduceți intervalul numeric, valorile separate prin virgulă și numele premiului conectându-le cu un : (coloană). Poate fi scris pe mai multe rânduri.",
    "usage3": "Istoric:\nDacă doriți să începeți loteria de la mijloc, introduceți numere separate prin virgulă.",
    "usage4": "Exemplu de specificație numerică) 1000-1200,1300,1400,1500 reprezintă un total de 204 articole, inclusiv 201 articole de la 1000 la 1200 și 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_ru.arb

{
	"@@locale":"ru",
	"@locale": {
		"description": "ロシア"
	},
	"start": "НАЧИНАТЬ",
    "setting": "Параметр",
    "candidate": "Кандидат",
    "prize": "Приз",
    "history": "История",
    "historyMainDraw": "Отображать историю лотерей на главной странице",
    "initial": "Исходный",
    "erase": "Стереть",

	"machineImageIndex": "Изображение машины",
    "machineSpeed": "Скорость лотерейного автомата",
    "machineSoundVolume": "Громкость звука машины",
    "prizeSoundVolume": "Громкость звука приза",
    "speakSoundVolume": "говорить громкость звука",
    "speakResult": "Ознакомиться с результатами лотереи",
    "voice": "Голос",
    "language": "Язык",
    "usage1": "Кандидат:\nУкажите варианты лотереи, используя числовой диапазон и номера, разделенные запятыми.",
    "usage2": "Приз:\nВведите числовой диапазон, значения, разделенные запятыми, и название приза, соединив их : (двоеточием). Можно написать в несколько строк.",
    "usage3": "История:\nЕсли вы хотите начать лотерею с середины, введите числа через запятую.",
    "usage4": "Пример числового указания) 1000-1200,1300,1400,1500 представляет собой всего 204 позиции, в том числе 201 позицию от 1000 до 1200 и 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_sk.arb

{
	"@@locale":"sk",
	"@locale": {
		"description": "スロバキア"
	},
	"start": "ŠTART",
    "setting": "Nastavenie",
    "candidate": "Kandidát",
    "prize": "cena",
    "history": "História",
    "historyMainDraw": "Zobraziť históriu lotérií na hlavnej stránke",
    "initial": "Počiatočné",
    "erase": "Vymazať",

	"machineImageIndex": "Obraz stroja",
    "machineSpeed": "Rýchlosť lotériového stroja",
    "machineSoundVolume": "Hlasitosť zvuku stroja",
    "prizeSoundVolume": "Hlasitosť zvuku ceny",
    "speakSoundVolume": "hovoriť hlasitosťou zvuku",
    "speakResult": "Prečítajte si výsledky lotérie",
    "voice": "Hlas",
    "language": "Jazyk",
    "usage1": "Kandidát:\nUrčite voľby lotérie číselným rozsahom a číslami oddelenými čiarkami.",
    "usage2": "Cena:\nZadajte číselný rozsah, hodnoty oddelené čiarkou a názov ceny tak, že ich spojíte s : (dvojbodkou). Dá sa písať na viacero riadkov.",
    "usage3": "História:\nAk chcete spustiť lotériu od stredu, zadajte čísla oddelené čiarkami.",
    "usage4": "Príklad číselnej špecifikácie) 1000-1200,1300,1400,1500 predstavuje spolu 204 položiek vrátane 201 položiek od 1000 do 1200 a 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_sv.arb

{
	"@@locale":"sv",
	"@locale": {
		"description": "スウェーデン"
	},
	"start": "START",
    "setting": "Miljö",
    "candidate": "Kandidat",
    "prize": "Pris",
    "history": "Historia",
    "historyMainDraw": "Visa lotterihistorik på huvudsidan",
    "initial": "Första",
    "erase": "Radera",

	"machineImageIndex": "Maskinbild",
    "machineSpeed": "Lotterimaskin hastighet",
    "machineSoundVolume": "Maskinens ljudvolym",
    "prizeSoundVolume": "Prisljudvolym",
    "speakSoundVolume": "tala ljudvolym",
    "speakResult": "Läs upp lotteriresultatet",
    "voice": "Röst",
    "language": "Språk",
    "usage1": "Kandidat:\nAnge lotterivalen genom numeriskt område och kommaseparerade nummer.",
    "usage2": "Pris:\nAnge det numeriska området, kommaseparerade värden och prisnamnet genom att koppla dem med ett : (kolon). Kan skrivas på flera rader.",
    "usage3": "Historik:\nOm du vill starta lotteriet från mitten, ange siffror separerade med kommatecken.",
    "usage4": "Exempel på numerisk specifikation) 1000-1200,1300,1400,1500 representerar totalt 204 artiklar, inklusive 201 artiklar från 1000 till 1200 och 1300, 1400, 1500.",

	"dummy": "dummy"
}

lib/l10n/app_th.arb

{
	"@@locale":"th",
	"@locale": {
		"description": "タイ"
	},
	"start": "เริ่ม",
    "setting": "การตั้งค่า",
    "candidate": "ผู้สมัคร",
    "prize": "รางวัล",
    "history": "ประวัติศาสตร์",
    "historyMainDraw": "แสดงประวัติลอตเตอรี่ในหน้าหลัก",
    "initial": "อักษรย่อ",
    "erase": "ลบ",

	"machineImageIndex": "ภาพเครื่อง",
    "machineSpeed": "ความเร็วเครื่องลอตเตอรี",
    "machineSoundVolume": "ระดับเสียงของเครื่อง",
    "prizeSoundVolume": "ระดับเสียงของรางวัล",
    "speakSoundVolume": "พูดระดับเสียง",
    "speakResult": "อ่านผลการออกรางวัลลอตเตอรี",
    "voice": "เสียง",
    "language": "ภาษา",
    "usage1": "ผู้สมัคร:\nระบุตัวเลือกลอตเตอรี่ตามช่วงตัวเลขและตัวเลขที่คั่นด้วยเครื่องหมายจุลภาค",
    "usage2": "รางวัล:\nป้อนช่วงตัวเลข ค่าที่คั่นด้วยเครื่องหมายจุลภาค และชื่อรางวัลโดยเชื่อมต่อกับ : (โคลอน) สามารถเขียนได้หลายบรรทัด",
    "usage3": "ประวัติ:\nหากคุณต้องการเริ่มหวยจากตรงกลาง ให้กรอกตัวเลขโดยคั่นด้วยลูกน้ำ",
    "usage4": "ตัวอย่างข้อกำหนดเชิงตัวเลข) 1000-1200,1300,1400,1500 แสดงถึงจำนวน 204 รายการ รวมทั้ง 201 รายการตั้งแต่ 1000 ถึง 1200 และ 1300, 1400, 1500",

	"dummy": "dummy"
}

lib/l10n/app_zh.arb

{
	"@@locale":"zh",
	"@locale": {
		"description": "中国"
	},
	"start": "开始",
    "setting": "环境",
    "candidate": "候选人",
    "prize": "奖",
    "history": "历史",
    "historyMainDraw": "在主页上显示抽奖历史",
    "initial": "最初的",
    "erase": "擦除",

	"machineImageIndex": "机器形象",
    "machineSpeed": "彩票机速度",
    "machineSoundVolume": "机器音量",
    "prizeSoundVolume": "奖品音量",
    "speakSoundVolume": "说话音量",
    "speakResult": "读出抽奖结果",
    "voice": "嗓音",
    "language": "语言",
    "usage1": "候选人:\n通过数字范围和逗号分隔的数字指定抽签选择。",
    "usage2": "奖品:\n输入数值范围、逗号分隔值和奖品名称(用 :(冒号)连接)。 可以写在多行上。",
    "usage3": "历史记录:\n如果您想从中间开始抽奖,请输入以逗号分隔的数字。",
    "usage4": "数值规格示例) 1000-1200,1300,1400,1500 表示共 204 项,其中 1000 至 1200 和 1300、1400、1500 共 201 项。",

	"dummy": "dummy"
}

AppStoreConnectで審査に通らなかった理由。よって仕様を変更して審査に通しました。

Hello,

Thank you for submitting your items for review. We noticed an issue with your submission that requires your attention.

Submission ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
App Name: 抽選機 Slot

We look forward to working with you to resolve the issues with the following items:

App Version

1.0.0 for iOS
Guideline 2.3.6 - Performance - Accurate Metadata

The rating you have selected is inconsistent with the content of your app. Since your app includes features that simulate gambling, your ratings must reflect a "Frequent/Intense" setting for "Simulated Gambling" in App Store Connect.

Next Steps

To resolve this issue, please update your Age Rating selections in App Store Connect to select "Frequent/Intense" setting for Simulated Gambling. The Age Rating selections can be found on the App Information page after selecting your app in App Store Connect.

Note: Apps must be rated accordingly for the highest level of content that the user is able to access in the app and apps with gambling services must be submitted by an organization, not by an individual.

Resources

- See how to set the app age rating in App Store Connect.
- Review our policies for age ratings on the App Store in App Store Review Guideline 2.3.6.

Hello,

In order to reduce fraudulent activity on the App Store and comply with government requests to address illegal online gambling activity, we are no longer allowing simulated gambling apps submitted by individual developers.

We found that you have submitted this app under an individual account and indicated in the Ratings section of App Store Connect that it contains Simulated Gambling. While you can no longer distribute gambling apps from this account, you may continue to submit and distribute other types of apps to the App Store.

If your app does not include gambling content, it would be appropriate to revise your app's rating in App Store Connect to more accurately reflect the content available in your app. Once you have updated your app's rating in App Store Connect, you can submit your app for review.

Going forward only verified accounts from incorporated business entities may submit gambling apps for distribution on the App Store. Visit the Enrollment page to learn more about enrolling an organization in the Apple Developer Program. For information on transferring an app to another developer account, please review the App transfer overview page in App Store Connect Developer Help.

As a reminder, the App Store Review Guidelines state that it is your responsibility to ensure your app complies with all legal requirements in any location where you make it available.

Best regards,

App Store Review



For details, next steps, and to ask questions about these issues, please visit the App Review page in App Store Connect.

Best regards,
App Store Review

TM and © 2023 Apple Inc.
One Apple Park Way, Cupertino, CA 95014.

Terms of Service | Privacy Policy | Contact Us | App Store Connect
このページでお気付きの点がございましたら
ご連絡いただけますと幸いです。
Eメール(任意)
送信内容
 

便利ウェブサイト