diff --git a/README.md b/README.md index 68a60ab..6f7a035 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ API Docs: [wd-textify.wmcloud.org/docs](https://wd-textify.wmcloud.org/docs) | `id` | string | Yes | Comma-separated entity IDs (for example: `Q42` or `Q42,Q2`). | | `pid` | string | No | Comma-separated property IDs to filter claims (for example: `P31,P279`). | | `lang` | string | No | Preferred language code (default: `en`). | -| `fallback_lang` | string | No | Fallback language code (default: `en`). | +| `fallback_lang` | string | No | Fallback language code (default: `en`). Use `any` to select the first available language when the requested language is unavailable. | | `format` | string | No | Output format: `json`, `text`, or `triplet` (default: `json`). | | `external_ids` | bool | No | Include `external-id` datatype claims (default: `true`). | | `all_ranks` | bool | No | Include all statement ranks instead of preferred/normal filtering (default: `false`). | diff --git a/main.py b/main.py index 1845081..c957307 100644 --- a/main.py +++ b/main.py @@ -105,6 +105,7 @@ async def get_textified_wd( - **all_ranks** (bool): If `true`, include preferred, normal, and deprecated statement ranks. - **qualifiers** (bool): If `true`, include qualifiers for claim values. - **fallback_lang** (str): Fallback language used when `lang` is unavailable. + Use `any` to select a deterministic available language from the entity. - **action_api_url** (str): Action API URL (default: `https://www.wikidata.org/w/api.php`). diff --git a/src/WikibaseLabel.py b/src/WikibaseLabel.py index b138807..d4e91a7 100644 --- a/src/WikibaseLabel.py +++ b/src/WikibaseLabel.py @@ -368,18 +368,29 @@ def get_lang_val(data, lang="en", fallback_lang=None): Args: data (dict): Label dictionary keyed by language. lang (str): Preferred language code. - fallback_lang (str | None): Optional fallback language code. + fallback_lang (str | None): Optional fallback language code. Use + ``"any"`` to select the first available language when the + requested language is unavailable. Returns: str: Selected label text, or an empty string when missing. """ - label = data.get(lang, data.get("mul", {})) - if fallback_lang and not label: - label = data.get(fallback_lang, {}) + if not isinstance(data, dict): + return "" + + lang = lang.lower() + fallback_lang = fallback_lang.lower() if fallback_lang else None + + label = data.get(lang) or data.get("mul") + if not label and fallback_lang: + if fallback_lang == "any": + label = next((data[key] for key in sorted(data) if data[key]), None) + else: + label = data.get(fallback_lang) if isinstance(label, str): return label - return label.get("value", "") + return label.get("value", "") if isinstance(label, dict) else "" @staticmethod def get_all_missing_labels_ids(data): diff --git a/tests/unit/test_wikibaselabel.py b/tests/unit/test_wikibaselabel.py index cffcdf4..14061e6 100644 --- a/tests/unit/test_wikibaselabel.py +++ b/tests/unit/test_wikibaselabel.py @@ -30,6 +30,16 @@ def test_get_lang_val_falls_back_to_mul_and_fallback_language(): assert WikibaseLabel.get_lang_val(labels_without_mul, lang="en", fallback_lang="fr") == "Etiquette" +def test_get_lang_val_falls_back_to_any_available_language(): + """It should select a stable available language with the wildcard fallback.""" + labels = { + "de": {"value": "Etikett"}, + "fr": {"value": "Etiquette"}, + } + + assert WikibaseLabel.get_lang_val(labels, lang="en", fallback_lang="any") == "Etikett" + + def test_get_all_missing_labels_ids_collects_nested_ids(): """It should collect IDs from nested property, unit, claim, and datavalue branches.""" payload = {