しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

Androidのアプリの国際化方法のメモ

Androidアプリの国際化対応のメモです。
下の手順で実施しました。

まずはres/valuesフォルダ以下にstrings.xmlを作成して下のように文字列リソースを作成しました。

<resources>
    <string name="app_name">メモ帳</string>
    <string name="yes">はい</string>
    <string name="no">いいえ</string>
</resources>

下のように直接記入していた文字列はstrings.xmlのリソースを使うようにしました。

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/test"
        android:title="削除" // → @string/deleteに置き換え
        android:icon="@drawable/ic_baseline_list_24" />
</menu>

kotlin上の文字列は下のようにgetStringでリソースから取得するようにしました。

editText.setText(getString(R.string.delete))

getStringはActivityやFragmentで使えるメソッドなのでそれ以外ではContextを経由して文字列を取得しました。

context.resources.getString(R.string.delete)

◯日のように数字が入る場合は%dを使ってあとから数字を入れられるようにしました。
文字列の場合は%sになります。

<resources>
    <string name="days">%d日間</string>
    <string name="day_of_week">%s曜日</string>
</resources>

Kotlin上では下のように第2引数に数字を入れる事で%dに数字が入ります。

context.resources.getString(R.string.days, 3)

一通りリソース化をしたら次は各言語用のstrings.xmlを作成します。
strings.xmlは各言語用のフォルダであるvalues-en以下に作成していきます。

翻訳作業はTranslations Editorで行いました。
下のようにキーごとの翻訳が見れるので抜け漏れが発生しにくくなります。

Translations Editorはstrings.xmlを開いた時の右上のOpen editorボタンから開く事が可能です。

画像ファイルについてもres/drawable-enのように各言語用のフォルダを作成して配置していくようです。
今後必要になれば使っていきたいと思います。

アプリのメタデータ翻訳ですがFastlaneを使っていたのでそっちで管理しました。
fastlane/metadata/android以下に各言語のフォルダを作ってそちらに各言語のタイトルやスクリーンショットを入れていきました。

3Dプリンタ用にG-code形式のファイルを作成する

3Dプリンタを購入したので出力の為に色々調べてみました。

今回購入したプリンタではG-codeという形式が必要でした。
調べた所3DプリンタはSTLやOBJという形式が一般的でG-codeはかなり珍しいようでした。

G-codeへの変換ですがSTL形式で作ってスライス(変換)する形が一般的なようでした。
今回はSlic3rというオープンソースのソフトを使って変換をしました。

slic3r.org

Blenderから直接エクスポートできないかも試したのですが対応していないようでした。

ソフトを開くと下のような画面になります。

メニューからSTL形式ファイルを開く事ができます。

あとは右側のボタンからG-codeとしてExportすれば完了です。

参考URL

【2021年版】フルカラー3Dプリンター用のOBJファイルを無料でダウンロードできるサイト5選 – ShareLab NEWS

STLをG-codeに変換 – KINGROON KP3S情報

SwiftPM(Swift Package Manager)対応ライブラリを作ってみる

SwiftPM対応ライブラリの作り方を調べてみました。

まずはライブラリのフォルダを作成してから下コマンドで初期化します。

swift package init

コマンドを実行すると下のようなフォルダやファイルが作られます。
Sourcesにはライブラリのコードを入れてTestsにはテスト用のコードを入れます。
Package.swiftはライブラリの設定を入力していきます。

initではtypeも指定でき、下のようにコマンドで実行可能なパッケージなども作る事ができます。
デフォルトはlibraryなのでライブラリを作りたい場合は何も指定する必要がありません。

  --type <type>           Package type: (default: library)
        library           - A package with a library.
        executable        - A package with an executable.
        tool              - A package with an executable that uses

ライブラリはXcodeで編集していきます。
下のようにPackage.swiftを開きます。

open Package.swift

あとはSourcesフォルダの中にコードを追加していきます。

public class MyClass {
    public init() {}
    
    public func test() {
        print("Hello.")
    }
}

コードの追加が終わったら公開します。
GitHubリポジトリを作ってからコードを公開します。

git init .
git remote add origin git@github.com:shimesaba9/MyLibrary.git
git add .
MyLibrary$ git commit -m "First Commit"
git push origin main

あとはリポジトリのURLを入れる事でライブラリを追加できます。
ローカルにあるライブラリの場合はAdd Localボタンで直接追加する事もできます。

ローカルのライブラリを追加した場合、Build Phasesからライブラリを追加します。

これでアプリからライブラリを使えるようになりました。

import MyLibrary

struct ContentView {
    func test() {
        MyClass().test()
    }
}

参考URL

Swift Packagesでライブラリを自作する方法 - Qiita

Swift Package Managerでライブラリをインストールする

パッケージ管理システムのSwift Package Managerを試してみました。

パッケージの追加はメニューの「File」 → 「Add Packages」から行います。

右上から検索をして目的のライブラリを探して追加します。
今回はFirebaseのインストールをしました。

Firebaseの場合は追加時に下のようにパッケージを選択します。

追加完了すると画面左側にパッケージ一覧が表示されます。

追加したパッケージはPackage Dependenciesから削除する事ができます。

パッケージの更新はFileのPackagesから行います。

FirebaseのIn-App Messagingでアプリ内にアラートを出す

今回は下URLを参考にFirebaseのIn-App Messagingを使ってアプリ内にアラートを出してみました。

【iOS】Firebase In-App Messaging を使ってアプリ内メッセージを送ってみた | DevelopersIO

まずはCocoaPodsでFirebaseをインストールします。

target 'MyApp' do
  use_frameworks!

  pod 'Firebase'
  pod 'FirebaseInAppMessaging', '~> 10.11.0-beta'
end

FirebaseInAppMessagingのバージョンはbetaがついているのでバージョン指定をしないと古いものをインストールすることになります。
こちら下URLを参考にしました。

Firebase In-App MessagingをCocoaPodsで導入するとビルドエラーになる | anz blog

次はFirebaseの初期設定を行います。
FirebaseのConsoleからプロジェクトを追加していきます。

プロジェクトの作成が終わったらiOSボタンからiOSアプリを追加していきます。

設定中にダウンロードしたGoogleService-Info.plistはプロジェクトに追加しておきます。

設定が終わったらAppDelegate.swiftにFirebaseの初期化処理を入れます。

import UIKit
import Firebase

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        
        return true
    }
}

これでアプリを起動するとFirebaseにイベントが来ます。

Firebaseの設定が終わったので次はIn-App Messagingを動かしていきます。

まずはメッセージを送るためにデバイスのIDを取得します。
Edit Schemeスキーマ編集画面に移動してArguments Passed On Launch-FIRDebugEnabledを追加します。

これでアプリを起動するとInstallation IDが取得できるので保存しておきます。

次はFirebase上でアラートを作っていきます。
まずはGoogle CloudでFirebase In-App Messaging APIを有効にします。

次にFirebaseのサイドメニューからMessaging画面に移動してIn-Appメッセージを作ります。

最初の画面でデザインやボタンのアクションの設定をします。

この画面の右側の「デバイスでテスト」に先程取得したInstallation IDを入れる事でテストする事ができます。

実際にテストをすると下のようにアプリ立ち上げるだけでアラートを表示できます。

アラートは他に表示条件も設定する事ができます。

他には表示のトリガーのイベントの設定やコンバージョンの設定など必要な設定が一通り選べるようになっています。
今回はTestEventというイベントが発生した時にアラートが出る設定にしてみました。

アプリ側では下のようにボタンを押したらTestEventを送れるようにしました。

import UIKit
import FirebaseAnalytics

class ViewController: UIViewController {
    @IBAction func tapButton() {
        Analytics.logEvent("TestEvent", parameters: nil)
    }
}

ボタンを押したら下のようにアラートを表示する事ができました。

In-App Messaging試してみたのですがアプリアップデートなしで手軽にアラート出せるのが良さそうです。
ただ表示回数は1人あたり1回/1日のようなのでそこは注意が必要そうです。

Macでサイバーエージェントの公開したLLMを動かしてみる

サイバーエージェントが日本語LLMを公開してくれたので動かしてみました。

www.cyberagent.co.jp

ライセンスはクリエイティブ・コモンズで、商用利用もできるようです(2023/05/20時点)。
詳しくは各LLMの詳細ページに参考にしてください。

huggingface.co

手順は先日の記事と同じです。

MacでPython+Anacondaを使ってDolly2.0を動かす - しめ鯖日記

まずは下コマンドで実行環境を作成します。

conda create --yes -n env-open-calm-7b
conda activate env-open-calm-7b
conda install python=3.10.10 --yes
pip install torch torchvision torchaudio accelerate transformers

環境を作ったらmain.pyを作成して下のように記述します。

import torch
from transformers import pipeline

model_name = "cyberagent/open-calm-7b"
generate_text = pipeline(model=model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, device_map="auto")
text = generate_text("東京でおすすめの観光地はどこ?")
print(text[0]['generated_text'])

実行すると下のような結果になりました。

東京でおすすめの観光地はどこ?
【2019】京都の紅葉スポット25選!見頃や

同時に下のような警告が出ました。
どうやらmax_lengthは20になっていて、それによって文字列が途中で途切れているようです。

UserWarning: Using `max_length`'s default (20) to control the generation length. This behaviour is deprecated and will be removed from the config in v5 of Transformers -- we recommend using `max_new_tokens` to control the maximum length of the generation.

max_new_tokensを追加して動かしてみました。

import torch
from transformers import pipeline

model_name = "cyberagent/open-calm-7b"
generate_text = pipeline(model=model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, device_map="auto", max_new_tokens=1000)
text = generate_text("東京でおすすめの観光地はどこ?")
print(text[0]['generated_text'])

結果は下の通りです。
ループに入ったような感じの動きをしました。

東京でおすすめの観光地はどこ?
【2019】京都の紅葉スポット25選!見頃や名所、ライトアップまで
【2019】京都の紅葉名所25選!見頃や穴場、ライトアップまで
【2019】京都の紅葉名所25選!見頃や穴場、ライトアップまで
【2019】京都の紅葉名所25選!見頃や穴場、ライトアップまで

temperatureも少し変えてみました。

model_name = "cyberagent/open-calm-7b"
generate_text = pipeline(model=model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, device_map="auto", temperature=0.8, max_new_tokens=1000)
text = generate_text("東京でおすすめの観光地はどこ?")
print(text[0]['generated_text'])

結果は下の通りです。
設定方法がおかしいのかtemperatureを変えても結果は同じでした。
この後0.1を指定したのですが同じ結果になりました。

東京でおすすめの観光地はどこ?
【2019】京都の紅葉スポット25選!見頃や名所、ライトアップまで
【2019】京都の紅葉名所25選!見頃や穴場、ライトアップまで
【2019】京都の紅葉名所25選!見頃や穴場、ライトアップまで
【2019】京都の紅葉名所25選!見頃や穴場、ライトアップまで

今度は別の質問をしてみました。

model_name = "cyberagent/open-calm-7b"
generate_text = pipeline(model=model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, device_map="auto", max_new_tokens=100)
text = generate_text("上司とうまくいかないんだけどどうすればいい?")
print(text[0]['generated_text'])

今度は悩みとは少し違う回答が返ってきました。

上司とうまくいかないんだけどどうすればいい?
『モンスト』こいつってマジでかわいそうなキャラだよな・・・
『モンスト』属性あってないのに高難度で使えるのってコイツらくらいじゃね?wwwww
『モンスト』よく、こんなクエスト周回してたな・・・
『モンスト』課金してても高難度コンテンツなんてやってない!?
あいつらの登場で最高潮に盛り上がってただろ!!モンストアニメ登場キャラ「ビナー」「ゼフォン」は激・獣神祭の新限定キャラに...

最初に問いについて、max_new_tokensと実行時間の関係を測定してみました。
測定は3回行い、平均実行時間を取得します。

max_new_tokens_list = [30, 40, 50, 100, 1000]
for max_new_tokens in max_new_tokens_list:
    print("max_new_tokens: ", max_new_tokens)

    for _ in range(3):
        start_time = time.time()
        model_name = "cyberagent/open-calm-7b"
        generate_text = pipeline(model=model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, device_map="auto", max_new_tokens=max_new_tokens)
        text = generate_text("東京でおすすめの観光地はどこ?")
        print(text[0]['generated_text'])
        print("実行時間:", time.time() - start_time, "秒")

結果は下の通りです。
各回の結果は()内に記入しました。
50まではmax_new_tokensに比例して伸びている事が分かります。
100以上では比例しないのは、100に到達する前に回答の生成が終わったからのようです。

max_new_tokens=30…62.6秒(59.1, 58.7, 69.9)
max_new_tokens=40…84.1秒(83.0, 84.5, 84.8)
max_new_tokens=50…100.6秒(100.4, 100.6, 100.8)
max_new_tokens=100…143.2秒(140.7, 141.2, 147.7)
max_new_tokens=1000…140.9秒(141.6, 141.0, 140.2)

現状はDolly2.0の方が精度が良さそうですが、リリースされたばかりという事もあり今後の改善が楽しみです。

MacでPython+Anacondaを使ってDolly2.0を動かす

商用利用もできるLLMのDolly2.0を試してみました。

まずはAnacondaをインストールします。
Anacondaはデータサイエンスや機械学習などで良く使われるツールで、便利なライブラリなどを含んだプラットフォームです。

Anacondaはインストーラーを使ってインストールしました。
最初はpyenvを使ったのですがうまくできなかったので公式サイトからインストールしました。

www.anaconda.com

次はDolly2.0を使ってみます。
今回は下URLを参考にしました。

qiita.com

まずはAnacondaでDolly用の仮想環境を作ってPythonをインストールします。

conda create --yes -n env-dolly-v2
conda activate env-dolly-v2
conda install python=3.10.10 --yes

次は必要なライブラリをインストールします。

pip install torch torchvision torchaudio accelerate transformers

最後にmain.pyを作って下のように記述します。
今回はKotlinでButtonの背景色を変える方法を聞いてみました。

import torch
from transformers import pipeline

model_name = "databricks/dolly-v2-12b"
generate_text = pipeline(model=model_name, torch_dtype=torch.bfloat16, trust_remote_code=True, device_map="auto")
text = generate_text("How to change the background color of Button in kotlin?")
print(text[0]['generated_text'])

あとはpythonでmain.pyを動かせば完了です。

python main.py

Dollyをダウンロードするので初回起動は非常に時間がかかります。

2回目以降も4分ほど時間がかかりました。
Activityモニタで見た所、メモリも20GB以上使っていました。

回答は下の通りです。
Buttonの初期化やbackgroundTintListのセット方法など違っているのですがそれっぽい答えは返ってきました。

To change the background color of Button in kotlin, you can use the property backgroudTintList.
For example:
Button().backgroundTintList(Color.rgb(255, 255, 255))

プログラミング以外の質問もしてみました。
今回は旅行先を尋ねました。

text = generate_text("Do you have any recommendations for destinations?")

旅行先についてはかなりそれらしい答えを返してくれました。

Recommended destinations vary based on the type of traveller and the purpose of visit. For solo travellers, Barcelona is a great destinations to kickstart your travel experience. The vibrant nightlife and artistic scene make it a great place to have some fun. For those who like a great culture experience, Italy can be a great option. From food, wine and art, it offers everything. For those who like to get away from the city and see the natural beauty, Arizona, United States is an option. With its diverse nature scenes, it offers something for everyone.

日本語でも聞いてみました。

text = generate_text("おすすめの旅行先があれば教えてください。")

回答は下の通りです。
旅行っぽい言葉を返してくれるのですが内容は支離滅裂でした。

ベトナムに言語根性のない人以外は不向きなのであまりおすすめできないかもしれませんが、日本を一時帰国させてからデッキンasusの港に出発する airports に Japan と printed でスペリングしていただきたい場所があります。私が行ったように検問で確認されただけでも便利ですね