プライマリ 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 のリソース管理システムをまったく使用せずにローカライズを行う必要がある場合があります。
これが良いアイデアである場合は、自由に上記のコードを使用して改善してください。