every Tech Blog

株式会社エブリーのTech Blogです。

Android で性別に応じて文法を変更する方法について

この記事は every Tech Blog Advent Calendar 2024 13 日目の記事です。

はじめに

こんにちは、DELISH KITCHEN でクライアントエンジニアを担当している kikuchi です。

普段会話をする際に、話す相手は誰か、言及する対象は人であるか物であるか、性別はどうか、といった様々な情報から微妙にニュアンスを変えて話すことがありますが、
もしアプリでユーザの特性によって文言を出し分ける、というような機能を実装する場合は、条件分岐が複雑化するなど多くの手間がかかってしまいます。
今回はそのユーザの特性の中でもユーザの性別 (文法上の性別) によって、簡単にアプリ上で表示する文言を切り替えることができる Grammatical Inflection API という機能を紹介したいと思います。

Grammatical Inflection API を使用することで、性別による複雑な条件分岐を実装する手間を省くことができます。

文法上の性別とは

一言で「文法上の性別」と記載しても少し分かりづらいと思いますので、具体例を記載したいと思います。

Android Developer サイト で本件について「フランス語でサービスに登録されていることをユーザに知らせるメッセージの例」があるため引用します。

  • Masculine-inflected form: 「Vous êtes abonné à...」 (English: 「You are subscribed to...」)
  • Feminine-inflected form: 「Vous êtes abonnée à...」 (English: 「You are subscribed to...」)
  • Neutral phrasing that avoids inflection: 「Abonnement à...activé」 (English: 「Subscription to ... enabled」)

この様に英語では文法が変わりませんが、フランス語では微妙に文法が変わっている (「abonné」と「abonnée」で差異がある) ことが分かります。
日本語でも「あなたは〇〇に登録しています」といった統一の文法になると思いますが、上記の様に文法上の性別への対応が必要な言語も存在するため、
多言語対応をする場合は言語に合わせて適切な文法を設定することがユーザに対しての適切なアプローチとなります。

Grammatical Inflection API の概要

こちらの API で提供される機能は大きく分けて 2 つあります。

  1. 文法上の性別を選択する
  2. 性別によって文字列のリソースを分ける

まず 1 についてですが、こちらで選択できる性別の修飾子は

  • 女性的 (feminine)
  • 男性的 (masculine)
  • 中性的 (neuter)

の 3 つが存在します。

そして 2 についてですが、Android は以前から言語で文字列のリソースを分けることができますが、そこに更に性別でもリソースを分けることができる様になります。
先程のフランス語を例にすると

  • フランス語、かつ女性的 : res/values-fr-feminine/strings.xml
  • フランス語、かつ男性的 : res/values-fr-masculine/strings.xml
  • フランス語、かつ中性的 : res/values-fr-neuter/strings.xml

といった分け方ができ、例えば 1 の機能で女性的 (feminine) を選択していると、res/values-fr-feminine/strings.xml のリソースから文字列が読み込まれる様になります。

なお、本 API ですが、

  • Android 14 以降の端末のみサポートされる
  • Android Studio Giraffe Canary 7 以降の環境のみ、性別のリソースの修飾子 (values-fr-masculine の masculine の部分) がサポートされる

といった制約があるため、事前に対象の OS を絞る、開発環境を新しくするといった対応が必要となります。

実装方法

本項目では具体的な実装方法について説明したいと思います。

文法上の性別を選択する

文法上の性別を選択する API の実装方法について説明します。

まずは AndroidManifest.xml で API を実施する Activity に宣言を追加します。

<activity
    android:name=".TestActivity"
    android:configChanges="grammaticalGender"  ← こちらを追加する
    android:exported="true">
</activity>

そして次に性別を選択する API を以下の様に実装します。

val gIM = getSystemService(requireContext(), GrammaticalInflectionManager::class.java)
gIM?.setRequestedApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_FEMININE)

GrammaticalInflectionManager のサービスにアクセスし、setRequestedApplicationGrammaticalGender メソッドを呼ぶのみとなります。
こちらで指定できる値は

  • Configuration.GRAMMATICAL_GENDER_FEMININE : 女性的
  • Configuration.GRAMMATICAL_GENDER_MASCULINE : 男性的
  • Configuration.GRAMMATICAL_GENDER_NEUTRAL : 中性的

となります。

本 API ですが、アプリの初回起動時のアンケート、あるいは設定画面などで性別を選択する UI を用意し、ユーザが選択したタイミングで性別を選択する API を実行する、といった使い方ができるかと思います。

なお、選択した性別を取得する API は以下の様に実装します。

val gIM = getSystemService(requireContext(), GrammaticalInflectionManager::class.java)
val grammaticalGender = gIM?.applicationGrammaticalGender

こちらも選択のケースと同様で、GrammaticalInflectionManager サービスにアクセスし applicationGrammaticalGender で値を取得するのみとなります。

性別によって文字列のリソースを分ける

次に性別によって文字列のリソースを分ける方法ですが、こちらは特にコードを書く必要はありません。
概要で説明した通り、性別のリソースファイルを用意するのみとなります。

●res/values-fr/strings.xml (言語のリソースファイル)
<resources>
    <string name="test">test</string>
</resources>

●res/values-fr-feminine/strings.xml (女性的のリソースファイル)
<resources>
    <string name="test">test_feminine</string>
</resources>

●res/values-fr-masculine/strings.xml (男性的のリソースファイル)
<resources>
    <string name="test">test_masculine</string>
</resources>

●res/values-fr-neuter/strings.xml (中性的のリソースファイル)
<resources>
    <string name="test">test_neuter</string>
</resources>

文法上の性別を選択する API で Configuration.GRAMMATICAL_GENDER_FEMININE を設定していた場合、test の識別子のリソースにアクセスすると
女性的のリソースファイルにアクセスするため、「test_feminine」という文字列が取得できる様になります。
なお、文法上の性別を選択する API を実行していない場合は言語のリソースファイルにアクセスするため「test」という文字列が取得できます。

実装としては以上となります。

注意点

言語のリソースファイルには全てのリソースの識別子が網羅されており、性別によって変化させたい文字列のみ性別のリソースファイルに定義する必要があります。
また言語のリソースファイルに定義されていないリソースの識別子を性別のリソースファイルに定義はできません。

具体例を記載します。

●res/values-fr/strings.xml (言語のリソースファイル)
<resources>
    <string name="test">test</string>
</resources>

●res/values-fr-feminine/strings.xml (性別のリソースファイル)
<resources>
    <string name="test">test_feminine</string>
</resources>

●res/values-fr-masculine/strings.xml (性別のリソースファイル)
<resources>
    <string name="test_aaa">test_masculine</string>
</resources>

このようなケースの場合、

  • values-fr-masculine に 「test」が無いが、values-fr に定義されているのでエラーにはならない (values-fr の「test」が読み込まれる)
  • values-fr-masculine に 「test_aaa」が定義されているが、values-fr に定義されていないためエラーになる

となるため、エラーを回避する場合は values-fr にも test_aaa を定義する必要があります。
こちらは大本のリソースファイルと言語のリソースファイル (values/strings.xml と values-fr/strings.xml) の関係と同様のルールとなります。

また、values-fr-neuter が存在しませんが、values-fr が存在しているのでエラーとはなりません。

まとめ

翻訳自体はかなり専門的な知識が必要となりますが、私自身海外のアプリを使用する際は翻訳が雑だと怪しいと感じ、逆に翻訳が行き届いていると丁寧なアプリだと感じることがあるので、
こういった細かい部分を丁寧に対応することがユーザの獲得、継続利用に繋がると考えています。

また日本語の場合でも、性別によって言葉を変えることでよりターゲットを絞った訴求ができることも考えられるため、一度この機能の導入を検討してみてはいかがでしょうか。