4

プライマリ APK以外の外部リソースからローカライズされたテキストを読み込む Android アプリケーションを開発する必要があります。
これは、第三者がアプリケーションの翻訳を独自に提供できるようにするためです。アプリケーションには現在、かなり多数の文字列 (~2,000) を含む 1 つの英語ローカライズがあります。

私は、Android のリソース システムから切り離したくありません。strings.xmlたとえば、他の Android アプリと同じように、主要言語にローカライズされた文字列を提供したいと考えています。

これを実現するために、android.content.res.Resources を拡張して 3 つのgetTextメソッドをオーバーライドするクラスを作成しました。オーバーライドする実装は、可能な場合は外部ローカリゼーション ソースからリソースを返し、そうでない場合は要求をsuper.getText()実装に転送します。

リソース ラッパー:

public class IntegratedResources extends Resources {

    private ResourceIntegrator ri;

    public IntegratedResources(AssetManager assets, DisplayMetrics metrics, Configuration config, ResourceIntegrator ri) {
        super(assets, metrics, config);
        this.ri = ri;
    }

    @Override
    public CharSequence getText(int id)
            throws NotFoundException {
        return ri == null ? super.getText(id) : ri.getText(id);
    }

    @Override
    public CharSequence getText(int id, CharSequence def)
            throws NotFoundException {
        return ri == null ? super.getText(id, def) : ri.getText(id, def);
    }

    @Override
    public CharSequence[] getTextArray(int id) 
            throws NotFoundException {
        return ri == null ? super.getTextArray(id) : ri.getTextArray(id);
    }
}

次に、Activity のコンテキストをラップする ContextWrapper 実装を作成しました。コンテキスト ラッパーの getResources() メソッドは、上記の IntegratedResources オブジェクトを返します。

ContextWrapper:

public class IntegratedResourceContext extends ContextWrapper {

    private IntegratedResources integratedResources;

    public IntegratedResourceContext(Activity activity, String packageName) 
    throws NameNotFoundException {
        super(activity);

        ResourceIntegrator ri = packageName == null ? null : new ResourceIntegrator(activity, packageName);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindow().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

        integratedResources = new IntegratedResources(activity.getAssets(), displayMetrics, 
                activity.getResources().getConfiguration(), ri);
    }

    @Override
    public Resources getResources() {
        return integratedResources;
    }
}

最後に、「ResourceIntegrator」クラスがあります。これは、指定されたインストール済みのサードパーティ ローカリゼーション APK からリソースを選択します。
必要に応じて、XML またはプロパティ ファイルからそれらを取得するために別の実装を作成することもできます。

リソースインテグレータ:

public class ResourceIntegrator {

    private Resources rBase;
    private Resources rExternal;
    private String externalPackageName;

    private Map<Integer, Integer> baseIdToExternalId = new HashMap<Integer, Integer>();

    public ResourceIntegrator(Context context, String externalPackageName) 
    throws NameNotFoundException {
        super();
        rBase = context.getResources();

        this.externalPackageName = externalPackageName;

        if (externalPackageName != null) {
            PackageManager pm = context.getPackageManager();
            rExternal = pm.getResourcesForApplication(externalPackageName);
        }
    }

    public CharSequence getText(int id, CharSequence def) {
        if (rExternal == null) {
            return rBase.getText(id, def);
        }

        Integer externalId = baseIdToExternalId.get(id);
        if (externalId == null) {
            // Not loaded yet.
            externalId = loadExternal(id);
        }

        if (externalId == 0) {
            // Resource does not exist in external resources, return from base.
            return rBase.getText(id, def);
        } else {
            // Resource has a value in external resources, return it.
            return rExternal.getText(externalId);
        }
    }

    public CharSequence getText(int id)
    throws NotFoundException {
        if (rExternal == null) {
            return rBase.getText(id);
        }

        Integer externalId = baseIdToExternalId.get(id);
        if (externalId == null) {
            // Not loaded yet.
            externalId = loadExternal(id);
        }

        if (externalId == 0) {
            // Resource does not exist in external resources, return from base.
            return rBase.getText(id);
        } else {
            // Resource has a value in external resources, return it.
            return rExternal.getText(externalId);
        }
    }

    public CharSequence[] getTextArray(int id)
    throws NotFoundException {
        if (rExternal == null) {
            return rBase.getTextArray(id);
        }

        Integer externalId = baseIdToExternalId.get(id);
        if (externalId == null) {
            // Not loaded yet.
            externalId = loadExternal(id);
        }

        if (externalId == 0) {
            // Resource does not exist in external resources, return from base.
            return rBase.getTextArray(id);
        } else {
            // Resource has a value in external resources, return it.
            return rExternal.getTextArray(externalId);
        }
    }

    private int loadExternal(int baseId) {
        int externalId;

        try {
            String entryName = rBase.getResourceEntryName(baseId);
            String typeName = rBase.getResourceTypeName(baseId);
            externalId = rExternal.getIdentifier(entryName, typeName, externalPackageName);
        } catch (NotFoundException ex) {
            externalId = 0;
        }

        baseIdToExternalId.put(baseId, externalId);
        return externalId;
    }
}

stackoverflow への私の質問は、上記の実装が良いアイデアであるかどうか、API を適切に使用しているかどうか、その設計が明日の Android の未知のバージョンに対して将来的に保証されているかどうかです。
これまで誰もこれを行っているのを見たことがなく、ドキュメントやウェブでこの問題を解決することについて何も見つけられないようです。

独立した第三者による翻訳を許可するという根本的な要件は、かなり重要です。現在、このアプリケーション用に数十の翻訳を内部で維持することは現実的ではありません。また、ユーザーが提供した翻訳の品質を精査する能力もありません。
この設計が非常に悪い考えであり、同様の代替手段が利用できない場合は、Android のリソース管理システムをまったく使用せずにローカライズを行う必要がある場合があります。

これが良いアイデアである場合は、自由に上記のコードを使用して改善してください。

4

2 に答える 2

1

スタックオーバーフローへの私の質問は、上記が良い考えかどうかです

Android にカスタムContextWrapper. getString()、などを手動で呼び出す場所であればどこでも機能するはずですが、アクティビティの 1 つを超えてAndroidgetText()がこれらのリソースにアクセスする場所でどのように機能するかはわかりません。次のように、自分で電話をかけることができない場所がたくさんあります。getText()

  • ホーム画面ランチャー
  • 設定でのアプリの情報
  • 最近のタスク リスト

さらに、新しい文字列をまったく追加しない予定がない限り、永続的なバージョン管理の問題が発生します。サードパーティの翻訳に新しい文字列のサポートを強制する方法はないため、翻訳済みの文字列と翻訳されていない文字列が混在するアプリになってしまいます。

APIが適切に使用されているかどうか

それは問題ないようです。

その設計は、明日の Android の未知のバージョンに対して将来的に保証されているかどうか

Android がリソース自体にアクセスする場所の数は、減少するどころか増加する可能性があります。

現在、このアプリケーション用に数十の翻訳を内部で維持することは現実的ではありません。また、ユーザーが提供した翻訳の品質を精査する能力もありません。

次に、自分で管理したい言語のみをサポートします。まず、前述したように、いくつかの点でこれが完全な解決策になるかどうかはわかりません。第二に、あなたのアプローチは、翻訳が不十分であると責められることはないと考えているようであり、非難されることはおそらく減るでしょうが、そのような非難をなくすことはできません. この点で、私はスクォンクに同意します。

より多くの翻訳を提供するというあなたの熱意には感心しますが、個人的には、この特定の道を進むつもりはありません.

于 2012-06-15T10:12:24.823 に答える
1

あなたのテクニックがレイアウトのリソース参照にはうまくいかないと思います。これらは LayoutInflater によって解決され、最終的にはリソースをロードするプライベート メソッドを呼び出す TypedArray 実装によって解決されます。

于 2012-09-23T23:03:53.177 に答える